响应式原理与proxy
vue2响应式核心Api:Object.defineProperty
推荐文章:https://yuchengkai.cn/docs/frontend/framework.html#数据劫持
如何深度监听对象
需要递归调用
WARNING
writable&value
与getter&setter
不可同时或交叉使用,否则会报错:Invalid property descriptor. Cannot both specify accessors and a value or writable attribute
- enumerable为false时,chrome输出对象时,查阅属性会呈现灰色。表示不可枚举
js
function observer(target) {
if(typeof(target) !== 'object' || target === null) return;
for(let key in target) {.
watchObj(target, key, target[key])
}
}
/*
数据描述符类(value,writable)
存取描述符类(get,set)
能与数据描述符或者存取描述符共存的共有属性(configurable,enumerable)
注意:数据描述符与存取描述符不能同时使用,否则会报错: TypeError: value appears only in data descriptors, get appears only in accessor descriptors
*/
function watchObj(target, key, value) {
observer(value) // 循环值时触发一次监听,如果不是对象则会return【递归】
Object.defineProperty(target, key, {
enumerable: true, // 可枚举(是否可通过for...in 或 Object.keys()进行访问。为false时, chrome 控制台打印显灰色, JSON.stringify()时仅序列化可枚举的属性)
configurable: true, // 可配置(是否可使用delete删除,是否可再次设置属性描述符【为false时, 可以设置其值;delete删除属性时返回false,且不会删除该属性;不可以再次设置defineProperty配置configurable:ture,否则报错】)
// value: '', // 任意类型的值,默认undefined
// writable: false, // 可重写. 为false时给属性赋值不会生效也不会报错
// 主要关注下面两个属性
get() {
console.log(this === target) // true:this指向target
return value // 通过闭包会一直缓存
},
set(newVal) {
value = newVal
updateView( ) // 伪代码,触发更新视图
}
})
}
监听数组的实现
【新建一个对象并执行Array原型,在对象本身扩展Array的7个方法,监听变动,最终触发的是Array原型方法】
js
let aryMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'] // 7个:前后增删4,排序与反排序2、插入删除1
let nativeArrayPrototype = Array.prototype
let newObj = Object.create(nativeArrayPrototype); // Object.create(xxx):创建一个对象并将该对象原型指向xxx,再扩展新的方法不会影响到原型
aryMethods.forEach((method)=> {
//将push, pop等封装好的方法定义在对象newObj的属性上。 注意:是属性而非原型属性
newObj[method] = function () {
console.log('监听到变动!');
return nativeArrayPrototype[method].apply(this, arguments); // 调用对应的原生方法并返回结果
};
})
// 使用:
let list = [1, 2 , 3]
list.__proto__ = newObj // 别忘了将数组的隐式原型指向我们封装好的变异方法
list.push(4) // 监听到变动!
console.log(list ) // [1, 2, 3, 4]
Object.defineProperty 缺点
1、只能劫持对象的属性, 无法监听数组变化
2、添加删除属性只能通过$set、$delete。否则视图不会更新
3、深度监听需要一次性递归
vue3响应式核心Api:Proxy
优点:
- 1、直接监听对象,而非属性
- 2、可以监听到数组的变化
- 3、Proxy返回新对象,我们可以只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性直接修改。