Vue2和Vue3不同
INFO
Vue2到Vue3的性能提升,原因不能简单概括为defineProperty到Proxy的转变。从本质上看,Proxy基于反向代理的性能反而比defineProperty差。
根本原因是diff算法的优化、patch等多原因,而不是Proxy特性本身。
响应式
Object.defineProperty
//Vue2 Object.defineProperty
const initData = {
value:1
}
const data = {}
Object.keys(initData).forEach(key=>{
Object.defineProperty(data,key,{
get(){
console.log('访问',key)
return initData[key]
},
set(val){
console.log('设置值',key)
initData[key] = val
}
})
})
// 控制台调试 设置data.value=3,发现data没变,initData变化了
this.$set(data,a,1)
$set的目的是给指定对象添加响应式属性后,并触发视图的更新
function set(target:Array<any> | Object,key:any,val:any):any{
//isUndef是判断target是不是等于undefined或者null
//isPrimitive是判断target的数据类型是不是string、number、symbol、boolean中的一种
if(process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
){
warn(`Cannot set reactive property on undefined,null,or primitive value:${{target:any}}`)
}
//数组的处理
if(Array.isArray(target) && isValidArrayIndex(key)){
target.length = Math.max(target.lenght,key)
target.splice(key,1,val) //splice(index,length,val)修改下标为index,长度为length的值为val
return val //当使用splice方法后,原target的被更新,触发Vue2的响应式
}
//对象,并且该属性原来已存在于对象中,则直接更新
if(key in target && !(key in Object.prototype)){
target[key] = val
return val
}
//vue 给响应式对象(比如data里定义的对象)都加了 _ob_ 属性
//若一个对象里有 _ob_ 属性,那么就说明这个对象是响应式对象,修改对象已有属性时就会触发页面渲染
//非 data 里定义的就不是响应式对象(以下是判断是否为响应式对象)
const ob = (target:any)._ob_
if(target._isVue || (ob && ob.vmCount)){
process.env.NODE_ENV !== 'product' && warn(
'Avoid adding reactive properties to a Vue instance or its root $date' +
'at runtime - declare it upfront in the data options.'
)
return val
}
//不是响应式对象
if(!ob){
target[key] = val
return val
}
//是响应式对象,进行依赖收集
defineReactive(ob.value,key,val)
//触发更新视图
ob.dep.notify()
return val
}
Proxy
结合Object.defineProperty,可以去说明vue2和vue3在响应式这块如何获取和修改
const initData = { value:1 }
const proxy = new Proxy(initData,{
get:function(target,key,receiver){
console.log('访问',key)
return Reflect.get(target,key,receiver)
},
set:function(target,key,value,receiver){
console.log('修改',key)
return Reflect.set(target,key,value,receiver)
}
})
// 控制台调试 设置proxy.value2 = 2,proxy和initData都变化了
proxy也有很多 ES6 的写法,通过 polifill 转成 ES5
衍生
:如何将 esNext => es5? 常用的是 babel 去转,国外主流使用 swc(Rust编写)