Vue3 setup详解
setup执行的时机
- 在beforeCreate之前执行(一次),此时组件对象还没创建;
- this是undefined,不能通过this来访问data/computed/methods/props;
- 其实所有的composition API相关回调函数中也都不可以;
setup的返回值
- 一般都返回一个对象:为模板提供数据,也就是模板中可以直接使用此对象中的所有属性/方法
- 返回对象中的属性会与data函数返回对象合并成为组件对象的属性
- 返回对象中的方法会与methods中的方法合并成功组件对象的方法
- 如果有重名,setup优先
- 注意:一般不要混合使用:methods中可以访问setup提供的属性和方法,但在setup方法中不能访问data和methods;setup不能是async函数:因为返回值不再是return的对象,而不是promise,模板看不到return对象中的属性数据
setup参数
- setup(props,context)/setup(props,{attrs,slots,emit})
- props:包含props配置声明且传入了所有属性的对象
- attrs:包含没有在props配置中声明的属性的对象,相当于this.$attrs
- slots:包含所有传入的插槽内容的对象,相当于this.$slots
- emit:用来分发自定义事件的函数,相当于this.$emit
演示代码
- 父组件
App
msg: {{msg}}
- 子组件
{{n}}
{{m}}
msg: {{msg}}
msg2: {{$attrs.msg2}}
reactive与ref细节
- 是vue3的composition API中最重要的响应式API
- ref用来处理基本类型的数据,reactive用来处理对象(递归深度响应式)
- 如果用ref对象/数组,内部会自动将对象/数组转换为reactive的代理对象
- ref内部:通过给value属性添加getter/setter来实现对数据的劫持
- reactive内部:通过使用Proxy来实现对对象内部所有数据的劫持,并通过Reflect操作对象内部数据
- ref的数据操作:在js中要.value,在模板中不需要(内部解析模板时会自动添加.value)
App
m1: {{m1}}
m2: {{m2}}
m3: {{m3}}
计算属性和监视属性
App
fistName:
lastName:
fullName1:
fullName2:
fullName3:
生命周期
与2.x版本生命周期相对应的组合API
。beforeCreate=>使用setup()
。create=>使用setup()
。beforeMount=>onBeforeMount
。mounted=>onMounted
。beforeUpdate=>onBeforeUpdate
。updated=>onUpdated
。beforeDestroy=>onBeforeUnmount
。destroyed=>onUnmounted
。errorCaptured=>onErrorCaptured
toRefs
- 把一个响应式对象转换成普通对象,该普通对象的每个property都是一个ref
- 应用:当从合成函数返回响应式对象时,toRefs非常有用,这样消费组件就可以在不丢失响应式的情况下对返回的对象进行分解使用
- 问题:reactive对象取出的所有属性值都是非响应式的
- 解决:利用toRefs可以将一个响应式reactive对象的所有原始属性转换为响应式的ref属性
App
foo: {{foo}}
bar: {{bar}}
foo2: {{foo2}}
bar2: {{bar2}}
ref获取元素
- 利用ref函数获取组件中的标签元素
- 功能需求:让输入框自动获取焦点
自定义hook函数
与vue2中mixins作用差不多,使用方法是直接在页面中引用即可
import { ref,onMounted } from "vue";
export default function() {
const x = ref(0);
const y = ref(1);
const clickHandler = (event:MouseEvent) => {
x.value = event.pageX;
y.value = event.pageY;
}
onMounted(() => {
window.addEventListener('click',clickHandler);
})
return {
x,
y
}
}
toRefs的使用
可以将响应式的对象转换成一个个ref变量
const person = reactive({
name: 'guozhiqiang',
age: '20',
sex: '男',
tag: '帅哥'
})
const a = toRefs(person);//返回一个ref变量组成的数组
a.tag.value = '大帅哥';
console.log(a.tag.value);//大帅哥
console.log(person.tag.value)//大帅哥
ref获取标签元素
const InputText = ref();//获取ref为InputText的元素
shallowReactive与shallowRef
浅拷贝与深拷贝的区别,shallowReactive只有对象第一层的数据是响应式的,可以引起视图更新
shallowRef其实就是特殊的shallowReactive,也是只监听第一层数据
这两个适用于层数较深的对象但只需要对象第一层需要响应式的需求,可以减少浏览器开销
const person = shallowReactive({
name: 'guozhiqiang',
age: '20',
sex: '男',
tag: '帅哥',
job: {
name: 'web 开发工程师'
}
})
person.name = 'shuaige';//数据更新时页面会变化
person.job.name = 'aa';//数据更新时页面不会变化
//shallowRef生成非递归响应数据,只监听第一层数据的变化
let state= shallowRef({
gf: {
f: {
s: {
d: 4
},
c: 3
},
b: 2
},
a: 1
})
readOnly 和 shallowReadonly
用于设置数据只读,readonly和shallowReadonly的区别就是shallowReadonly只有第一层数据只读,其他数据可改变
const person = readonly({
name: 'guozhiqiang',
age: '20',
sex: '男',
job: {
name: 'web 开发工程师'
}
})
person.name = 'a'//错误,目标代理对象为只读
const person2 = shallowreadonly({
name: 'guozhiqiang',
age: '20',
sex: '男',
job: {
name: 'web 开发工程师'
}
})
person.name = 'a'//错误,只有第一层数据为只读
person.job.name = 'a'//可以
toRaw 和 markRaw
- toRaw:
作用:将一个由reactive生成的响应式对象转为普通对象。
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
- markRaw:
作用:标记一个对象,使其永远不会再成为响应式对象。
应用场景:
有些值不应被设置为响应式的,例如复杂的第三方类库等。
当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
let data = {
name: "张三",
age: 18,
likeFood: {
fruits: {
apple: "苹果",
},
},
}
// 将普通对象变为响应对象
let person = reactive(data);
// 将响应对象变为普通对象
let p = toRaw(person)
//标记p,永远不能成为响应对象
markRaw(p)
toRef的使用
ref:是复制,修改响应式数据不会影响以前的数据,数据发生变化,界面就会自动更新。
toRef:是引用,修改响应式数据会影响以前的数据,数据发生变化,界面就会不会更新。
应用场景
如果想让响应式数据和以前的数据关联起来,并且更新响应式数据之后还不想更新UI,那么就可以使用toRef
setup() {
const state = reactive({
age:5,
money:100
})
//把响应式数据state对象中的某个属性age变成了ref对象了
const age = toRef(state,'age')
//把响应式对象中的某个属性使用了ref进行包装,变成了一个ref对象
const money = ref(state.money)
}
customRef的使用
自定义hook防抖的函数,value传入的数据,将来数据的类型不确定,所以,用泛型delay防抖的间隔事件,默认是200毫秒
function userDebounceRef(value: T,delay = 200){
let timeOutId:number
return customRef((track,trigger)=>{
return {
get() {
//告诉Vue追踪数据
track()
return value
}
set(newValue:T) {
//清除定时器
cleatTimeout(timeOutId)
//开启定时器
timeOutId = setTimeout(()=>{
value = newVlaue
//告诉Vue更新界面
trigger()
},delay)
}
}
})
export default defineComponent({
name:'App',
setup() {
const keyword = userDebounceRef('abs',500)
}
return {
keyword,
}
})
Provide 和 inject实现祖孙级组件通信
在setup中使用
provide('color','red');
const w = inject('color');
响应式数据的判断
//isRef: 检查一个值是否为一个ref对象
console.log(isRef({}))
//isReactive: 检查一个值是否为一个reactive对象
console.log(isReactive({}))
//isReadonly: 检查一个值是否为一个readonly对象
console.log(isReadonly({}))
//isProxy: 检查一个值是否为reactive和readonly代理的对象
console.log(isProxy({}))