赞
踩
cat ‘不是内部或外部命令…’ 用type代替cat
https://github.com/sunniejs/vue-h5-template
最新的ECMAScript标准定义了8种数据类型:
// 原始数据类型: Boolean;Null;Undefined;Number;BigInt;String;Symbol
let isDone: boolean = false
// es6 中 number还支持二进制和八进制
let age: number = 10
let binaryNumber: number = 0b1111
// 字符串类型,也可使用es6中的模板字符串
let firstName: string = 'wly'
let message: string = `hello, ${firstName}`
// undefinde 和 null 所有类型的子类型
let u: undefined = undefined
let n: null = null
// undefined类型可以赋值给number类型的变量[官方这么讲,但是我码出来就是报错,暂时还未知]
let num: number = undefined
// any类型
let notSure: any = 4
notSure = 'maybe a string'
notSure = false
notSure.myName // 在任意值上访问任何属性都是允许的
notSure.getName() // 也允许调用任何方法
// 数组
let arrOfNumbers: number[] = [1,2,3] //定义数字类型的数组
arrOfNumbers.push(4)
function test() {
console.log(arguments) //arguments 是一个对应于传递给函数的参数的类数组对象。
}
function func1(a, b, c) {
console.log(arguments[0]) // 1
console.log(arguments[1]) // 2
console.log(arguments[2]) // 3
}
func1(1, 2, 3)
// 元组Tuple 声明几个类型就能写几个,不能多写少些
let user: [string, number] = ['wly', 123]
user.push(12) //但是可以push,push的值只能是string、number-联合类型
// 定义一个接口 IPerson
interface IPerson {
readonly id: number; //readonly只读属性
name: string;
age?: number; //?可选属性,这一项可有可无,否则不可多项和少项
}
// 定义一个变量viking, 他的类型是IPerson
let viking: IPerson = {
id: 1,
name: 'viking'
}
// 约定输入,约定输出
function add(x: number, y: number): number {
return x + y
}
let result = add(1, 2)
// z是个可选参数
const add2 = (x: number, y: number, z?: number): number => {
if(typeof z === 'number') {
return x + y + z
} else {
return x + y
}
}
// 函数本身的类型,注意在函数类型里面返回类型是个箭头
const add3: (x: number, y: number, z?: number) => number = add2
// 接口里面返回类型是个冒号
interface ISum {
(x: number, y: number, z?:number): number
}
let add4: ISum = add2
let str = 'str'
// union types-联合类型 中间用竖线来分割
let umberOrString: number | string
umberOrString = 1
umberOrString.toString() // 只能访问此联合类型里共有的属性或方法
// type assertions-类型断言 as作为关键字-当编辑器无法判断我的代码,但是本人很清楚,我把它成string,你也可以用string方法
function getLength(input: string | number): number {
const str = input as string
if(str.length) {
return str.length
} else {
const number = input as number
return number.toString().length
}
}
//type guard-类型守卫 在不同条件的分支里面,智能缩小范围,降低代码出错
function getLength2(input: string | number): number {
if(typeof input === 'string') {
return input.length
} else {
return input.toString().length
}
}
// 数字枚举 关键词enum, 我们定义了一系列得方向,这里得值,枚举成员会被赋值为从 0 开始递增的数字
enum Direction {
Up,
Down,
Left,
Right
}
console.log(Direction.Up) // 0
console.log(Direction[0]) // Up // 这个枚举还做了反向映射
enum Direction2 {
Up = 'up',
Down = 'down',
Left = 'left',
Right = 'right'
}
const value = 'up'
if(value === Direction2.Up) {
console.log('go up!!')
}
// 常量枚举
const enum Direction3 {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
const value3 = 'UP'
if(value3 === Direction3.Up) {
console.log("go up!!!")
}// An highlighted block
var foo = 'bar';
注意字符串枚举跟常量枚举最本质得区分就是js中,字符串枚举只会执行Direction3.Up,其余在Direction3得其他属性值都不会执行
function echo<T>(arg: T):T {
return arg
}
// const str: string = 'str'
// const result = echo(str)
const result1 = echo(123)
function swap<T, U>(tuple: [T, U]):[U, T] {
return [tuple[1], tuple[0]]
}
const result2 = swap(['string', 123])
// 约束泛型
function echoWithArr<T>(arg: T[]):T[] {
console.log(arg.length)
return arg
}
const arrs = echoWithArr([1, 2, 3])
interface IWithLength {
length: number
}
function echoWithArrLength<T extends IWithLength>(arg: T): T {
return arg
}
const str = echoWithArrLength('str')
const obj = echoWithArrLength({ length: 10, width: 10 })
const arr2 = echoWithArrLength([1, 2, 3])
echoWithArrLength(13) // number里面没有length属性,所以报错
// 类的公有类型(public)和私有类型(private)
class Queue<T> {
private data: T[] = [];
push(item: T) {
return this.data.push(item)
}
pop() {
return this.data.shift()
}
}
const queue = new Queue<number>()
queue.push(1)
const popedValue = queue.pop()
if(popedValue) {
console.log(popedValue.toFixed())
}
// 泛型和interface
interface KeyPair<T, U> {
key: T
value: U
}
let kp1: KeyPair<number, string> = {key: 1, value: 'string'}
let kp2: KeyPair<string, number> = {key: 'str', value: 2}
let arr: number[] = [1, 2, 3]
let arrTwo: Array<number> = [1, 2, 3]
// 类型别名 和 交叉类型
import { type } from "os"
let sum: (x: number, y: number) => number
const result = sum(1, 2)
type PlustType = (x: number, y: number) => number
let sum2: PlustType
const result2 = sum2(2,3)
// 支持联合类型
type StringOrNumber = string | number
let result3: StringOrNumber = '123'
result3 = 123
const str: 'name' = 'name'
const number: 1 = 1
// 字符串字面量
type Direcions = 'Up' | 'Down' | 'Left' | 'Right'
let toWhere: Direcions = 'Left'
// 交叉类型
interface IName {
name: string
}
type IPerson = IName & {age: number}
let person: IPerson = {name: '123', age: 123}
注:这两个文件需要同时在编辑器中打开,不然axio会报错
// 它里面没有任何的实际实现代码,只有类型声明
// 只有类型 - 比如 interface,funciton 或者 class 等等
// declare function axios(url: string): string
interface IAxios {
get: (url: string) => string;
post: (url: string, data: any) => string
}
declare const axios: IAxios // declare 声明类型
declaration-files.ts
// axios('test.url')
axios.get('url')
axios.post('url', {name: 'viking'})
npm i axios
declaration-files.ts
import axios from 'axios'
axios.get('ulr')
axios.post('url', {name: 'viking'})
//第一种
// caculator.d.ts
type IOperator = 'plus' | 'minus'
type ICalculator = (operator: IOperator, numbers: number[]) => number
declare const calculator: ICalculator
// declaration-files.ts
calculator('minus', [1,2])
// 第二种
// caculator.d.ts
type IOperator = 'plus' | 'minus'
interface ICalculator {
(operator: IOperator, numbers: number[]): number;
plus: (numbers: number[]) => number;
minus: (numbers: number[]) => number;
}
declare const calculator: ICalculator
export default calculator // 也可以使用es6
// declaration-files.ts
import calculator from './calculator'
calculator.minus([1,3])
将第二种的caculator.d.ts代码,挪到node_modules下的@types中创建文件夹caculator里面建立文件index.d.ts里面(作为模块来使用)
// declaration-files.ts
import calculator from 'calculator'
const a: Array<number> = [1, 2, 3]
// 大家可以看到这个类型,不同的文件中有多处定义,但是它们都是 内部定义的一部分,然后根据不同的版本或者功能合并在了一起,一个interface 或者 类多次定义会合并在一起。这些文件一般都是以 lib 开头,以 d.ts 结尾,告诉大家,我是一个内置对象类型欧
const date: Date = new Date()
date.getTime()
const reg = /abc/
// 我们还可以使用一些 build in object,内置对象,比如 Math 与其他全局对象不同的是,Math 不是一个构造器。Math 的所有属性与方法都是静态的。
reg.test('abc')
Math.pow(2, 2)
// DOM 和 BOM 标准对象
// document 对象,返回的是一个 HTMLElement
let body: HTMLElement = document.body
// document 上面的query 方法,返回的是一个 nodeList 类型
let allList = document.querySelectorAll('li')
allList.keys()
//当然添加事件也是很重要的一部分,document 上面有 addEventListener 方法,注意这个回调函数,因为类型推断,这里面的 e 事件对象也自动获得了类型,这里是个 mouseEvent 类型,因为点击是一个鼠标事件,现在我们可以方便的使用 e 上面的方法和属性。
document.addEventListener('click', (e) => {
e.preventDefault()
})
interface IPerson {
name: string,
age: number
}
let viking: IPerson = { name: 'viking', age: 20 }
type IPartial = Partial<IPerson>
let viking2: IPartial = { name: 'viking'}
type IOmit = Omit<IPerson, 'name'> // IPerson name属性忽略掉
let viking3: IOmit = { age: 20 }
{
"files": ["test.ts", "test2.d.ts"],
"compilerOptions": {
"outDir": "./output",
"module": "ESNext",
"target":"ES5",
"declaration": true
}
vue-ui 图形化界面创捷项目
vite脚手架 不同于vue-cli的创新
// vue 3.0推荐vscode种volar插件,注将原来vetur禁掉
// 查看项目下的eslint版本
在这里插入图片描述
vite:https://cn.vitejs.dev/
vscode中之前用的插件是vetur,vue3.0用的插件是volar
// 注:ref定义变量读取的时候写法是 该变量名.value
<template>
<h1>{{ count }}</h1>
<button @click="increase">ddddd</button>
<h2>{{ double }}</h2>
</template>
<script lang="ts">
// ref 是一个函数,它接受一个参数,返回的是一个神奇的 响应式对象。我们初始化的这个0作为参数包裹到这个对象中去,在未来可以检测到改变并作出对应的响应
import { defineComponent, ref, computed } from 'vue';
export default defineComponent({
name: 'App',
setup() {
const count = ref(0)
const double = computed(() => {
return count.value * 2
})
const increase = () => {
count.value++
}
return {
count,
increase,
double
}
}
});
</script>
<style></style>
使用ref还是reactive可以选择准则
(1)就像刚才的原生javascript的代码一样,像你平常写普通的js代码选择原始类型和对象类型一样来选择使用ref还是reactive
(2)所有场景使用reactive,但要记得使用toRefs保证reactive
对象属性保持响应性
<template>
<h1>{{ count }}</h1>
<button @click="increase">ddddd</button>
<h2>{{ double }}</h2>
</template>
<script lang="ts">
import { defineComponent, computed, reactive, toRefs } from 'vue';
interface DataProps {
count: number;
double: number;
increase: () => void
}
export default defineComponent({
name: 'App',
setup() {
const data: DataProps = reactive({
count: 0,
increase: () => {
data.count++
},
double: computed(() => data.count * 2)
})
const refData = toRefs(data)
return {
...refData
}
}
});
</script>
<style>
</style>
ref:原始类型(number, string, boolean, undefined, null)和对象都可以进行相应的数据做响应式处理
const ref1 = ref(0) //ok
const ref2 = ref({ a: 0 }) //ok
// ref可以处理原始类型的值,对于引用类型值也可。如果ref函数传递一个对象,那么这个对象要通过reactive()这个方法将其转换成深层次的响应式对象,换句话来说也就是内部本质还是调用了reactive()方法
reactive:该方法是不允许传递原始类型的值,它并没有和ref一样检测到原始类型之后进行做对应的转换
const reactive1 = reactive(0) // not ok
const reactive2 = reactive({ a: 0 }) //ok
// reactive1这种写法不会报错,页面也能渲染出来,但是没有办法改变它,失去了意义,对于reactive只能是对象最主要的原因在于在vue内部的相应式的实现,是依据proxy实现的,但是proxy不适用于基本类型数据
ref和reactive区别: ref对于基本类型和引用类型都可以,但是reactive只适用于引用类型
数据访问方式中ref:无论是原始类型还是对象,访问数据都需要通过.value的形式进行,更新数据的话也是通过.value
const ref1 = ref(0)
console.log(ref1.value) // 0
const ref2 = ref({ a: 0 })
console.log(ref2.value.a) // 0
ref1.value = 1
console.log(ref1.value) // 1
<div>{{ ref1 }}</div>
数据访问方式中reactive:由于是proxy代理的对象数据,可以直接更新和访问数据
const reactive1 = reactive({ num : 0})
console.log(reactive1.num) // 0
TS类型:ref
import { ref, Ref } from 'vue'
const ref1: Ref<number> = ref(0)
TS类型:reactive
import { reactive } from 'vue'
const reactive1: { num: number } = reactive({ num: 0 })
watch监听方式:ref
const ref1 = ref(0)
watch(ref1, () => {
console.log("changed")
})
// 注:ref中原始类型数据,这样写没啥问题当ref1.value改变时能被监听到
// 但如果是对象则需要深度监听才能监听到深层
const ref2 = ref({ a: 1 })
watch(ref2, () => {
console.log('changed ref2')
}, { deep: true })
watch监听方式:reactive
const reactive1 = reactive({ a: 1 })
watch(reactive1, () => {
console.log('changed reactive1')
})
ref和reactive总结:
vue 2.0 -> vue 3.0
beforeCreate -> use setup()
created -> use setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptured
//调试 debugger钩子函数
onRenderTracked
onRenderTriggered
watch数据源可以是ref(包括计算属性)、响应式对象、getter函数、或多个数据源组成的数组
const simplePerson = ref('张三')
// 情景一
// 数据源为RefImpl
watch(simplePerson, (newVal, oldVal) => {
console.log("simplePerson==", newVal) // 张三
}, {
immediate: true
})
// 情景二
// 数据源为'张三'
watch(simplePerson.value, (newVal, oldVal) => { //非法数据源,监听不到且控制台告警
console.log("simplePerson.value==", newVal) // undefined
}, {
immediate: true
})
ref值为引用类型,等价于:person.value = reactive({name: ‘张三’})
// 2、ref值为引用类型,等价于:person.value = reactive({name: '张三'})
const person = ref({
name: '张三'
})
// 情景三
// 数据源为RefImpl,但是.value才是响应式对象,所以要加deep
watch(person, (newVal, oldVal) => {
console.log("person==", newVal) // Proxy {name: '张三'}
}, {
immediate: true,
deep: true // 必须设置,否则监听不到内部变化
})
// 情景四
// 数据源为响应式对象
watch(person.value, (newVal, oldVal) => {
console.log('person.value==', newVal) // Proxy {name: '张三'}
}, {
immediate: true
})
// 情景五
//数据源为'张三'
watch(person.value.name, (newVal, oldVal) => { // 非法数据源,监听不到且控制警告
console.log('person.value.name==', newVal) // undefined
}, {
immediate: true
})
// 情景六
// 数据源为getter函数,返回基本类型
watch(
() => person.value.name,
(newVal, oldVal) => {
console.log("person.value.name==getter函数=", newVal) // '张三'
}
, {
immediate: true
})
ref值包含嵌套的引用类型,等价于:complexPerson.value = reactive({name: ‘张三’, info: {age: 18}})
const complexPerson = ref({name: '张三', info: {age: 18}})
// 情景七
// 数据源为响应式对象(在vue3中状态都是默认深层响应式的)
watch(complexPerson.value.info, (newVal, oldVal) => {
console.log("complexPerson.value.info===", newVal) // Proxy {age: 18}
},{
immediate: true
})
// 情景八
// 数据源为getter函数,返回相应式对象
watch(
() => complexPerson.value.info,
(newVal, oldVal) => {
console.log("complexPerson.value.info=getter函数==", newVal) // Proxy {age: 18}
}
, {
immediate: true,
})
const reactivePerson = reactive({name: '张三', info: {age: 18}})
// 情景九
// 数据源为响应式对象
watch(reactivePerson, (newVal) => {
console.log("reactivePerson==", newVal) // Proxy {name: '张三', info: {age: 18}}
},{
immediate: true
})
总结:
1、在Vue3中状态都是默认深层响应式的(情景七),嵌套的引用类型在取值(get)时一定是返回Proxy响应式对象
2、watch数据源为响应式对象时(情景四、七、九),会隐式的创建一个深层侦听器,不需要再显示设置deep: true
3、情景三和情景八两种情况下,必须显示设置deep: true,强制转换为深层侦听器
4、情景五和情景七对比下,虽然写法完全相同,但是如果属性值为基本类型时是监听不到的,尤其是ts类型声明为any时,ide也不会提示告警,导致排查问题比较费力
5、所以精确的ts类型声明很重要,否则经常会出现莫名其妙的watch不生效的问题
6、ref值为基本类型时通过get\set拦截实现响应式;ref值为引用类型时通过将.value属性转换为reactive响应式对象实现;
7、deep会影响性能,而reactive会隐式的设置deep: true,所以只有明确状态数据结构比较简单且数据量不大时使用reactive,其他一律使用ref
watch、watchEffect区别:
(1)、执行时机:watchEffect是立即执行的,在页面加载时会主动执行一次,来收集依赖;而watch是惰性地执行副作用,它不会立即执行,但可以配置immediate,使其主动触发。
(2)、参数不同:watchEffect只需传递一个回调函数,不需要传递侦听的数据,它会在页面加载时主动执行一次,来收集依赖;而watch至少要有两个参数(第三个参数是配置项),第一个参数是侦听的数据,第二个参数是回调函数。
(3)、结果不同:watchEffect获取不到更改前的值;而watch可以获取更改前和更改后的值。
// 侦听一个单一源
// (1)、直接侦听一个ref
const count = ref(0)
watch(count, (count, prevCount) => { // 基本类型写法
/**...*/
},{immediate: true}) // watch是惰性地执行副作用,它不会立即执行,但可以配置 immediate,使其主动触发
// (2)、侦听一个getter
const state = reactive({ count: 0 })
watch(() => state.count, (count, prevCount) => { //watch引用类型写法
})
// watchEffect
const bb = reactive({ count: 0 })
watchEffect(() => {
console.log(bb.count)
})
// watch 简单应用
watch(data, () => {
document.title = 'update' + data.count
})
// watch的两个参数,代表新的值和旧的值
watch(refData.count, (newValue, oldValue) => {
console.log("oldValue=",oldValue)
console.log("newValue", newValue)
document.title = 'update' + data.count
})
// watch多个值,返回的也是多个值的数组
watch([greetings, data], (newValue, oldValue) => {
console.log("oldValue", oldValue)
console.log("newValue", newValue)
document.title = 'updated' + greetings.value + data.count
})
// 使用greetings的写法watch reactive 对象中的一项
watch([greetings, () => data.count], (newValue, oldValue) => {
console.log('oldValue', oldValue)
console.log("newValue", newValue)
document.title = 'updated' + greetings.value + data.count
})
vue3的hooks函数相当于vue2的mixin,不同在与hooks是函数
vue3的hooks函数可以提高代码的复用性,可以在不同组件中利用hooks函数
例:
1、在src目录下创建一个hooks文件夹
2、创建文件名.ts
useMousePosition.ts
import { ref, onMounted, onUnmounted} from 'vue'
function useMousePosition() {
const x = ref(0) // x绑定响应式数据
const y = ref(0)
const updateMouse = (e: MouseEvent) => {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => {
document.addEventListener('click', updateMouse)
})
onUnmounted(() => {
document.removeEventListener('click', updateMouse)
})
return {x, y}
}
export default useMousePosition
在***.vue中
<template>
<div>
<h1>x{{x}}</h1>
<h1>y{{y}}</h1>
</div>
</template>
<script lang="ts">
import { defineComponent,ref, computed, reactive, toRefs, watch, onMounted, onUnmounted} from 'vue';
import useMousePostion from './hooks/useMousePosition'
export default defineComponent({
name: 'App',
setup() {
const { x, y } = useMousePostion()
return {
x,
y
}
}
});
</script>
import { reactive, toRefs, onMounted, onUnmounted } from 'vue'
interface UseMouse {
x: number,
y: number
}
function useMousePosition() {
const useMouse: UseMouse = reactive({
x: 0,
y: 0
})
const updateMouse = ((e: MouseEvent) => {
useMouse.x = e.pageX
useMouse.y = e.pageY
})
onMounted(() => {
document.addEventListener('click', updateMouse)
})
onUnmounted(() => {
document.removeEventListener('click', updateMouse)
})
return {
...toRefs(useMouse)
}
}
export default useMousePosition
script setup>是在单文件组件(SFC)中使用组合式API的编译时语法糖。相比于普通的
// 需要将setup attribute添加到<script>代码块上
<script setup>
console.log('hello script setup')
</script>
// 里面的代码会被编译成组件setup()函数的内容。这意味着与普通的<script>只在组件被首次引入的时候执行一次不同,<script setup>中的代码会在每次组件实例被创建的时候执行
顶层的绑定会被暴露给模板
// 当使用 <script setup> 的时候,任何在<script setup>声明的顶层的绑定(包括变量、函数声明,以及import引入的内容)都能在模板中直接使用
<template>
<div @click="log">{{ msg }}</div>
</template>
<script setup>
// 变量
const msg = 'hello!'
// 函数
function log() {
console.log(msg)
}
</script>
// import 导入的内容也会以同样的方式暴露。意味着可以在模板表达式中直接使用导入的helper函数,并不需要通过methods选项来暴露它
<script setup>
import { capitalize } from './helper'
</script>
<template>
<div>{{ capitalize('hello') }}</div>
</template>
// 响应式状态需要明确使用响应式API来创建。和setup()函数中返回值一样,ref值在模板中使用的时候会自动解包
<template>
<div @click="count++">{{ count }}</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
// <script setup>范围里的值也能被直接座位自定义组件的标签名使用
<template>
<ChildrenComponent />
</template>
<script setup>
import ChildrenComponent from './ChildrenComponent.vue'
</script>
// 由于组件被引用为变量而不是作为字符串来注册的,在<script setup>中要使用动态组件的时候,就应该使用动态的:is来绑定
<template>
<component :is="Foo"/>
<component :is="someCondition ? Foo : Bar"/>
</template>
<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
const someCondition = true
</script>
// 一个单文件组件可以通过它的文件名被其自己所引用。例如: 名为FooBar.vue的组件可以在其模板中用<FooBar/>引用它自己
// 请注意这种方式相比import导入的组件优先级更低。如果有命名的import导入和组件的推断名冲突了,可以使用import别名导入
import { FooBar as FooBarChild } from './components'
// 可以使用带点的组件标记,例如<Foo.Bar>来引入嵌套在对象属性中的组件。这在需要从文件中导入多个组件的时候非常有用
<template>
<Form.Input>
<Form.label>label</Form.label>
</Form.Input>
</template>
<script setup>
import * as form './from-components'
</script>
不需要引入
// 在<script setup>中必须使用defineProps和defineEmits API来声明props 和 emits,他们具备完整的类型推断并且在<script setup>中直接可用的
<script setup>
const props = defineProps({
foo: String
})
const emit = defineEmits(['change', 'delete'])
</script>
// defineProps 和 defineEmits 都是只在<script setup>中才能使用得编译器宏。他们不需要导入且会随着<script setup>处理过程一同被编译掉
// defineProps 接收与props 选项相同得值,defineEmits也接收emits选项相同得值
// defineProps 和 defineEmits 在选项传入后,会提供恰当得类型推断
// 传入到defineProps 和 defineEmits 得选项会从setup中提升到模块得范围。因此,传入得选项不能引用在setup范围中声明得局部变量。这样做会引起编译错误。
``````javascript
// 子组件中
<template>
<div @click="tableChange('21d32332')">
dfd {{ businessTableUuid }}
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
businessTableUuid: {
type: String,
required: true
},
})
const emit = defineEmits(['update:businessTableUuid'])
// const businessTableUuid = ref('')
// const businessTableFieldUuid = ref('')
const tableChange = (businessTableUuid) => {
emit('update:businessTableUuid', businessTableUuid) // 注意:update:businessTableUuid之间没有空格
}
</script>
// 父组件中
// 在vue3.0中 v-model:businessTableUuid代替了vue2.0中:businessTableUuid.sync
<template>
<div>
<Home2 v-model:businessTableUuid="businessTableUuid"></Home2>
</div>
</template>
<script setup>
import { onMounted, nextTick, watch, watchEffect, ref, reactive, defineExpose } from "vue";
const businessTableUuid = ref('ddfff')
</script>
// 使用<script setup>得组件是默认关闭得,也即通过模板ref或者$parent获取到得组件得公开实例,不会暴露任何在<script setup>中声明得绑定
// 为了在<script setup>组件中明确暴露出去得属性,使用defineExpose编译器宏
//父组件引用子组件得事件或值
//子组件中
<script setup>
import { ref, defineExpose } from 'vue'
const count = ref(100)
function changeCount() {
count.value = count.value + 1
}
defineExpose({
count,
changeCount
})
</script>
//父组件中
<children ref="child"/>
const child = ref(null)
function getExposes() {
console.log("getExposes==",child.value.count)
}
// 当父组件通过模板ref的方式获取到当前组件的实例,(ref会和在普通实例中一样被自动解包)
// 在<script setup>使用slots 和 attrs 的情况应该是罕见的,可以在模板中通过$slots和$attrs来访问它们,可以分别用useSlots 和 useAttrs 两个辅助函数
<script setup>
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
</script>
// useSlots 和 useAttrs与setupContext.slots 和 setupContext.attrs等价值,同样也能在普通的组合式API中使用
// <script setup>中可以使用顶层await。结果代码会被编译成async setup()
<script setup>
const post = await fetch('/api/post/1').then(r => r.json())
</script>
// await的表达式会自动编译成在await之后保留当前组件实例上下文的格式
// async setup()必须与Suspense组合使用,Suspensen目前还是处于实验阶段的特性
<template>
<div>123</div>
</template>
<script setup>
import { ref } from 'vue'
const theme = ref({
color: 'red'
})
</script>
<style lang="less" scoped>
div {
color: v-bind('theme.color')
}
</style>
<style scoped>
.a :deep(.b) {
}
</style>
修改插槽过来的组件的样式
<style scoped>
:slotted(.box) {
color: blue
}
</style>
全局样式: 通常新建一个style标签,不加scoped,:global就是一种解决方案
<style lang="less" scoped>
:global(.box) {
color: red;
}
</style>
<style>
/* 全局样式 */
</style>
<style scoped>
/* 局部样式 */
</style>
npm install eslint
npx eslint --init // eslint初始化
// 根目录下创建 .eslintrc.js
module.exports = {
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:vue/vue3-essential",
"plugin:@typescript-eslint/recommended"
],
"overrides": [
],
"parser": "vue-eslint-parser",
"parserOptions": {
"parser":"@typescript-eslint/parser",
"ecmaVersion": "latest",
"ecmaFeatures": {
jsx: true
}
},
"plugins": [
"vue",
"@typescript-eslint"
],
"rules": {
'space-before-function-paren': 0,
'no-console': 0,
}
}
npm install prettier eslint-config-prettier
// 其中process报错解决方法是
npm i @types/node
// 在vite.config.ts中添加
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
define: {
'process.env': {} // 新添加的部分
}
})
// https://router.vuejs.org/zh/introduction.html
npm install vue-router
// 在src文件夹下创建router文件夹里面文件名为index.ts
/**
* createRouter 这个为创建路由的方法
* createWebHashHistory 这个就是vue2中路由的模式,这里的是hash模式,这个还可以是createWebHistory等
* RouteRecordRaw 这个为要添加的路由记录,也可以说是routes的ts类型
*/
import { createRouter, createWebHashHistory } from 'vue-router';
const routes = [
{
path: '', // 重定向。默认首页是加载home组件
redirect: '/home'
},
{
path: '/home',
name: 'home',
component: () => import('@/views/home.vue'),
meta: {
title: '首页'
}
},
{
// 匹配所有路径 vue2使用* vue3使用/:pathMatch(.*)*或/:pathMatch(.*)或/:catchAll(.*)
path: '/:pathMatch(.*)*',
name: '404',
component: () => import('@/views/404.vue')
}
]
const router = createRouter({
history: createWebHashHistory(), // hash模式:createWebHashHistory,history模式:createWebHistory
routes: routes
})
export default router
//main.ts中的文件
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router' // 添加的router
const app = createApp(App)
app.use(router) // 添加的router
app.mount('#app')
https://cn.vuejs.org/guide/introduction.html#api-styles
element组件库安装,注意vue3.0不适用于element-ui,用的是element-plus
并且注意 import ‘element-plus/dist/index.scss’
各种报错
此时耐着性子往下看文档,在vite.config.ts中添加相关内容,并且将 import 'element-plus/dist/index.scss’去除
// 在tsconfig.json文件的“compilerOptions”中
{
"compilerOptions": {
……
"paths": {
"@/*": [
"src/*"
]
}
},
……
}
// 在tsconfig.node.json的文件"compilerOptions"下添加
{
"compilerOptions": {
……
"allowSyntheticDefaultImports": true
}
}
// 在vite.config.js文件中
……
import path from 'path' // 不按照上述修改 tsconfig.node.json 文件会报错
export default defineConfig({
plugins: [
vue(),
……
],
resolve: {
alias: {
'~': path.resolve(__dirname, './node_modules/'),
'@': path.resolve(__dirname, './src'),
api: path.resolve('./src/api'),
components: path.resolve('./src/components'),
views: path.resolve('./src/views'),
router: path.resolve('./src/router'),
}
}
})
https://pinia.vuejs.org/zh/getting-started.html
之前使用vuex进行状态管理,那么pinia相对于一个类似于的插件,是新一代轻量级状态管理插件
npm install pinia
// 安装完成后再src下创建文件夹strore,下再创建index.ts
// index.ts内容如下:
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
// defineStore()的返回值进行任意命名,最好使用store的名字,同时以`use`开头且以`Store`(比如`useUserStore`、`useIndexStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
// // 写法一
export const useCounterStore = defineStore('counter', {
// 相当于vue的data
state: () => {
return {
count: 1,
userList: [] as UserInfo[],
user: null as UserInfo | null
}
},
// 相当于vue的computed,在getters中使用了this则必须手动指定返回值类型,否则类型推导不出来
getters: {
double: (state) => state.count * 6
},
// 相当于vue的methods,在actions中不能使用箭头函数,因为箭头函数绑定外部this
actions: {
increment() {
this.count++
}
}
})
// // 写法二
// // ref() 就是state属性
// // computed() 就是getters
// // function() 就是actions
export const useCounterStore = defineStore('counter', () => {
const count = ref(1)
const double = computed(() => {
return count.value * 6
})
function increment() {
count.value++
}
return {count, double, increment}
})
// 在正常页面应用store
import { storeToRefs } from 'pinia';
import { useCounterStore } from '@/store/index' // 引入store内容
const store = useCounterStore()
// `count`是响应式refs
// 这也将由插件添加的属性创建refs
// 同时会跳过任何action或非响应式(非ref响应式)属性
const { count, double } = storeToRefs(store)
function rest() {
store.$reset() // 重置 state
}
function changeStateValue() {
store.$patch((state: any) => {
state.userList.push({
name: 'hahaha',
age: 20
})
state.count = 100
})
}
// $subscribe()方法侦听state及变化,相当于watch()
// { detached: true }作为第二个参数,当该组件被卸载时,它将自动删除
store.$subscribe((mutation: any, state: any) => {
console.log("mutation, state=====",mutation, state.count)
}, {
detached: true
})
<el-button class="header-nav-btn" @click="() => store.increment()">{{double}}注册{{count}}</el-button>
<el-button @click="rest">重置</el-button>
<el-button @click="changeStateValue">更改state</el-button>
<div>userList值:{{userList}}</div>
问题:
在使用vue3.2和vite2.0+开发一个移动端H5,测试时发现很多低版本的安卓手机浏览器出现白屏的现象,而ios机型基本上是好的,原因是很多低版本浏览器并不支持原生ESM导入的方式,下面给出解决方案:
兼容原生ESM的浏览器:
默认情况下,Vite 的目标浏览器是指能够 支持原生 ESM script 标签 和 支持原生 ESM 动态导入 的。作为参考,Vite 使用这个 browserslist 作为查询标准:
Chrome >=87
Firefox >=78
Safari >=13
Edge >=88
解决方案
通过插件 @vitejs/plugin-legacy 来自动生成传统浏览器的 chunk 及与其相对应 ES 语言特性方面的 polyfill。兼容版的 chunk 只会在不支持原生 ESM 的浏览器中进行按需加载。
安装: npm install @vitejs/plugin-legacy
在vite.config.js中
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import dns from 'dns'
dns.setDefaultResultOrder('verbatim')
import { resolve } from 'path' // vite版本不支持commonJs所以不能写成const path = require('path')
import legacy from "@vitejs/plugin-legacy"; // 新增!!!!!!!!!!!!
export default defineConfig({
// publicPath: './',
plugins: [vue(),
legacy({ // 新增!!!!!!!!!!!!
targets: [
"Android > 39",
"Chrome >= 60",
"Safari >= 10.1",
"iOS >= 10.3",
"Firefox >= 54",
"Edge >= 15",
"ie >= 11",
],
additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
})
],
define: {
'process.env': {}
},
resolve: {
alias: {
'~': resolve(__dirname, './node_modules/'),
'@': resolve(__dirname, './src'),
'assets': resolve(__dirname, './src/assets'),
'components': resolve(__dirname, './src/components'),
'common': resolve(__dirname, './src/common'),
'api': resolve(__dirname, './src/api'),
'views': resolve(__dirname, './src/views'),
'styles': resolve(__dirname, './src/styles'),
'store': resolve(__dirname, './src/store'),
}
},
server: {
proxy: {
'/columns': {
target: 'http://apis.sss.com/api/',
changeOrigin: true
},
},
},
})
注:该内容还未对api进行封装,后续再来补充
npm install axios
// 在src下创建api文件夹,创建两个子文件index.ts和http.ts
// index.ts
import { http } from './http'
import { ref } from 'vue'
export const dataLoading = ref(true)
export const getData = async () => {
const errorMsg = ref('')
const result = ref([])
dataLoading.value = true
await http
.get('/xxxxx/getData') // 待访问的服务接口
.then((res) => {
dataLoading.value = false
result.value = res.data
errorMsg.value = ''
})
.catch((err) => {
dataLoading.value = false
result.value = []
errorMsg.value = err.message
})
return {
result,
errorMsg
}
}
// https.ts
import axios from 'axios'
const env = process.env.NODE_ENV // 此处调用process会报错,在后配置项解决
const apis = {
production: 'http://localhost:8480',
staging: 'http://localhost:8480',
development: 'http://localhost:8480'
}
const vueAPI = apis[env as keyof typeof apis]
export const http = axios.create({
baseURL: `${vueAPI}/api`,
timeout: 10000,
headers: {
accept: 'application/json',
'Content-Type': 'application/json'
}
})
// 请求拦截
http.interceptors.request.use(
function (request) {
console.log('声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/318607
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。