对象修改会触发对象的get set 方法,在方法内进行拦截,但是Proxy只能拦截到目标对象自身属性的变化,所以进行递归去遍历对象的对象再去拦截,去实现响应式
1. 首先可以先了解
2.vue2.0 与 vue3.0的双向绑定原理
- Vue2通过使用 Object.defineProperty 来劫持对象属性的 geter 和 seter 操作,当数据发生改变发出通知。
- Vue3通过ES6的新特性proxy来劫持数据,当数据改变时发出通知。
- Vue2无法检测精确数组对象变化。
- vue3可以检测到对象/数组内部数据的变化,更精准的变更通知。
- vue2使用 Vue.set 来给对象新增一个属性时,这个对象的所有watcher 都会重新运行;
- vue3只有依赖那个属性的 watcher 才会重新运行 vue3默认进行懒观察。
- vue2.0数据一开始就创建了观察者,数据很大的时候,就会出现问题 。
- vue3中进行了优化只有用于渲染初始化可见部分的数据,才会创建观察者,效率更高 。
3.proxyES6新特性
大家都知道在vue3中响应式原理有原先的object.defineProperty换成了es6的新特性Proxy。
Proxy到底是什么
可以简单的理解为一个拦截器,他可以对任何对象的绝大部分行为和操作进行干涉,也就是我们常说的拦截。一提到拦截是不是就有感觉了。有了拦截不就可以实现响应式了嘛!也的确如此,对new Proxy返回的代理对象进行大部分操作如增删改读都可以被拦截到,我们就可以在其中做一些其他的事情。Proxy当然不止这些功能,他提供了多达13种行为的拦截,具体的可以自行查阅文档
- proxy和object.defineProperty的区别在哪儿?
我们都知道object.defineProperty只能拦截到对象自身以有的属性,而后续新增的属性是无法拦截到的,所以vue2提供了$set等一系列的api来帮助完善响应式。并且object.defineProperty并没有办法拦截到数组的一些变更,而vue2则采用了重写js种能修改源数据的数组api来实现数组的响应式,这些问题在proxy中都不存在了,这就是二者区别以及他的出现解决了vue2的哪些问题。
所以 对象修改会触发对象的get set 方法,在方法内进行拦截,但是Proxy只能拦截到目标对象自身属性的变化,所以进行递归去遍历对象的对象再去拦截,去实现响应式。
- vue3中的王牌响应式api reactive和ref
两个api都是包装数据用的,使数据变为响应式,唯一的不同点就是reactive用于包装对象和数组ref则可以用于包装各种类型。经过ref包装后的值需要通过.value的方式获取。其实ref在包装对象和数组类型的时候也是调用的reactive,具体的步骤看后续实现就行。
实现一个reactive
const reactiveHandler = {
get(target,key){
console.log('拦截到了读取数据',key)
return Reflect.get(target,key)
},
set(target,key,val){
console.log('拦截到了更新或新增数据',key)
return Reflect.set(target,key,val)
},
deleteProperty(target,key){
console.log('拦截到了删除数据',key)
return Reflect.deleteProperty(target,key)
}
}
function reactive(target) {
if(target&&typeof target==='object'){
if(Array.isArray(target)){
target.forEach((d,index)=>{
target[index] = reactive(d)
})
}else{
Object.keys(target).forEach(d=>{
target[d] = reactive(target[d])
})
}
return new Proxy(target,reactiveHandler)
}
return target
}
实现一个ref(运用到对象自带的get 和set 方法)
get 和set可以指定一个属性名,当我们在访问这个属性名的时候就会触发get和set方法。就可以实现拦截的效果拉。
{
_num:0,
get num(){
console.log('劫持到了get')
return this._num
},
set num(val){
console.log('劫持到了set',val)
this._num= val
}
}
function ref(target) {
return{
_value:reactive(target),
get value(){
console.log('劫持到了get')
return this._value
},
set value(val){
console.log('劫持到了set',val)
this._value = val
}
}
}