setup
特点:
- 它是一个组合api,只能是一个同步函数
- 生命周期位于beforeCreate和created之间,因此也意味着,在它里面无法访问到data和mounted,
- this = undefined;
- 需要返回一个对象 (在setup函数中定义的变量和方法需要 return 出去的 不然无法再template中使用)
- setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新。但是,因为 props 是响应式的,你不能使用 ES6 解构,因为它会消除 prop 的响应性。
props:['test']
setup(props,context){
//const {test} = props //错
const {
test} = toRefs(props) //对
const {
attrs, slots, emit }= context //对
return {
test
}
}
reactive
用来实现响应式更新ui页面,它的参数是{}/[],
如果是其他对象(new Date)之类的,则需要给它重新赋值,才会响应式更新
// state.time = newTime;
实现
原理其实就是对每一层的object进行递归监听
function reactive(obj) {
if(typeof obj === 'object'){
if(obj instanceof Array){
obj.forEach((item,index)=>{
if(typeof item === 'object'){
obj[index] = reactive(item)
}
})
}else{
for (let key in obj){
let item = obj[key]
if(typeof item === 'object'){
obj[key] = reactive(item)
}
}
}
return new Proxy(obj,{
get(obj,key){
return obj[key]
},
set(obj,key,value){
obj[key] = value;
return true//如果obj是数组的的话,它会改变两次,一次是value,一次是length ,不然会报错
}
})
}else{
console.warn(`${
obj} must be object`)
}
}
ref
一般用于创建基本类型的数据,它的本质其实也是reactive
所以在setup中要使用num的时候,需要通过.value修改,
const num = ref(0); // const num = reactive({value:0})
num.value = 2;
在<template>
标签中使用,则不需要,vue会自动添加上.value,解析之前,会判断是否为ref类型,通过当前数据的__v_isref属性是否为true判断
import {
onMounted,ref} from 'vue'
<div ref="box"></div>
setup(){
let box = ref(null)
onMounted(()=>{
console.log(box.value); //获取到dom值 <div ref="box"></div>
})
return {
box}
}
实现
function ref(val) {
reactive({
value:val})
}
递归监听
把每一层都包装成proxy对象,当数据量很大的时候,很耗性能
非递归监听(只有在监听数据量比较大的时候 )
shallowReactive只会监听第一层,如果第一层没变,则不会更新UI界面,
shallowRef 本质也是shallowReactive({value:}),所以value也是他的第一层
shallowReactive实现
function shallowReactive(obj){
return new Proxy(obj,{
get(obj,key){
return obj[key]
},
set(obj,key,value){
obj[key] = value
console.log('更新UI界面');
return true //如果obj是数组的的话,它会改变两次,一次是value,一次是length ,不然会报错
}
})
}
shallowRef的实现
function shallowRef(val) {
shallowReactive({
value:val})
}
所以需要改变它第一层value的数据,才会更新页面,或者是通过triggerRef主动触发,只提供triggerRef,没提供triggerReactive
state.value.a.b = 1
triggerRef(state),
只有通过ref或者reactive包装之后的对象修改才会更新UI
1.通过原始数据的不会
let obj = {
a:1}
let state = reactive(obj)
obj.a = 2
//state.a = 2 obj.a = 2
虽然数据改了,但是ui界面不会同步更新
2.或者通过toRaw获取到的原始数据修改也不会更新UI
let state = ref(2)//ref本质是reactive({value:})
let state1 = toRaw(state.value)
state1.a = 3
3.通过markRaw(),不让数据被追踪,所以之后修改了数据,UI也不会更新
let obj = {
a:1}
obj = markRaw(obj)
let state = reactive(obj)
应用场景:
当UI界面不需要更新的时候,因为每次修改UI界面更新其实是挺消耗性能的,这样可以减少重绘回流的次数
ref和toRef的区别
ref
let obj = {
a:1}
let state = ref(obj.a)
state.value.a = 3
//state变了,原始obj不变,UI变
toRef
let obj = {
a:1}
let state = toRef(obj,'a')
state.value.a = 3
//state变,原始obj变,ui不变
ref => 它的本质是复制,修改不会影响以前的数据,UI会变
toRef => 它的本质是引用,修改会影响到以前的数据,但是,UI不会改变
toRefs => 跟toRef一样,只是接收参数不一样
let obj = {
a:1,b:2}
let state = toRefs(obj,'a','b') //等于 let state = toRefs(obj)
state.a.value = 3
state.b.value = 3
应用场景: 如果想让响应式数据和原始数据关联起来,并且更新之后不想更新到UI,则可以使用
customRef
返回一个ref对象,可以显式的控制依赖追踪和触发相应
function myRef(value) {
return customRef((track,trigger)=>{
return {
get(){
track();//告诉VUE 这里的数据是需要追踪的
return value
},
set(newValue){
value = newValue
trigger()//告诉vue触发页面更新
}
}
})
}
readOnly
用于创建一个只读的数据,它是递归只读
let state = readOnly({
age:1,attr:{
age:1}})
const与readOnly的区别
const 赋值保护,不能给变量重新赋值
readOnly 属性保护,不能给属性重新赋值
readOnly实现
function readOnly(obj) {
return new Proxy(obj,{
get(obj,key){
return obj[key]
},
set(){
}
})
}
shallowReadOnly
用于创建一个只读的数据,不过只是第一层只读,第二层还是可以更改的,但UI不会更新
isReadOnly 是否是readOnly/shallowReadOnly
转载:https://blog.csdn.net/weixin_44730897/article/details/117028838