vue2 数据双向绑定
实现方法
- Object.definedProperty()进行数据劫持
- 结合发布订阅方式
由ES5提供 Object.defineProperty 缺点
- 只能劫持对象的属性, 初始化时,需要对每个对象每个属性进行遍历,如果属性值还是对象,则还需要深度遍历;即使没有使用这个属性,也会将该属性进行响应式处理,性能不高;
- 只能监听到对象属性已有数据是否被修改;无法监听到对象属性的新增和删除;
- 无法监听到数组的变化,例如,利用数组下标添加数据时,无法实施响应。
- 其在实现数组响应式时,使用了一些hack(触发视图更新),把无法监听数组的情况通过重写数组来实现响应,也仅限于数组的pop,push,shift,unshift,splice,sort,reverse七个方法,其他方法无法检测到。
vue3 数据双向绑定
实现方法
- Proxy代理,使用ref 或者reactive 将数据转化为响应式数据
proxy 优点
- 可以劫持整个对象,而非对象属性,并返回一个新的对象;
- 代理对象可以劫持对象属性的访问、新增、删除等操作,不需要初始化时遍历所有属性;
- 如果有多层属性嵌套,只有在访问到某个属性的时候才会递归处理下一级属性;
- 可以监听到数组变化,例如索引,length…
- 不仅可以代理对象,还可以代理数组,还可以代理动态增加的属性。
缺点
- 存在兼容性问题,不能用polyfill来兼容
- polyfill主要抚平不同浏览器之间对Js实现的差异
实现案例
- 实例引用于:https://blog.youkuaiyun.com/weixin_44700978/article/details/118769759
const toProxy = new WeakMap(); // 存放的是代理后的对象
const toRaw = new WeakMap(); // 存放的是代理前的对象
function trigger() {
console.log('触发视图更新')
}
function isObject(target) {
return typeof target === 'object' && target !== null;
}
function reactive(target) {
if (!isObject(target)) return target;
const proxy = toProxy.get(target)
// 如果代理表中已经存在了 就把这个结果返回
if (proxy) return proxy
// 如果这个对象已经被代理过了 就直接返回
if (toRaw.has(target)) return target;
const handlers = {
set(target, key, value, receiver) {
if (target.hasOwnProperty(key)) {
trigger()
}
return Reflect.set(target, key, value, receiver)
},
get(target, key, value, receiver) {
const res = Reflect.get(target, key, receiver)
if (isObject(target[key])) {
return reactive(res)
}
return res;
},
deleteProperty(target, key) {
return Reflect.deleteProperty(target, key)
}
}
const observed = new Proxy(target, handlers)
toProxy.set(target, observed);
toRaw.set(observed, target)
return observed
}
const obj = {
name: 'xxx',
arr: [1, 2, 3]
}
let p = reactive(obj)
p = reactive(p)
p = reactive(p)
p.arr.push(4)
console.log(p) //触发视图更新 { name: 'xxx', arr: [ 1, 2, 3, 4 ] }
Reflect解析地址:https://blog.youkuaiyun.com/qq_40963664/article/details/118788697
- Reflect是ES6为操作对象而提供的新API,proxy对象的方法在Reflect上都能找到;
- new WeakMap() 是弱引用,在内存不足的时候垃圾回收机制才会回收,可以作为缓存。