vue数组响应式原理
2021-04-07 本文已影响0人
miao8862
vue2中Object.defineProperty响应式只对对象有效,对数组无效,所以对数组做额外处理。我们知道,会改变数组本身的方法只有7个:sort, push, pop, slice, splice, shift, unshift,所以可以通过重写这些方法来达到数组响应式
解决方案:
1. 找到数组原型
2. 覆盖那些能够修改数组的更新方法,让他们在修改数组同时,还可以通知更新
3. 将得到的新的原型设置到数组实例原型上
4. 对数组内部元素实现响应式
// 实现数组响应式
// 1. 替换数组原型中7个方法
const originalProto = Array.prototype
// 克隆体原数组原型
const arrayProto = Object.create(originalProto)
// 可修改数组的7个方法 , 'sort'
const changeMethods = ['push', 'pop', 'shift', 'unshift', 'slice', 'splice', 'sort']
// 2. 在克隆的原型上,覆盖那些能够修改数组的更新方法,让他们在修改数组同时,还可以通知更新
changeMethods.forEach(method => {
arrayProto[method] = function() {
// 进行原始操作
originalProto[method].apply(this, arguments)
// 覆盖操作:增加更新通知
console.log(`数组正在执行${method}方法`);
}
})
// 对象响应化
function defineReactive(obj, key, value) {
Object.defineProperty(obj, key, {
get() {
console.log('获取' + key);
return value
},
set(newVal) {
if(newVal !== value) {
// console.log(newVal);
// console.log(JSON.stringify(obj[key]));
console.log(`正在改变${key}值:从${obj[key]}变为${newVal}`)
value = newVal
}
}
})
}
function observer(obj) {
// 不是对象或者为null,不做响应式,结束
if(typeof obj !== 'object' || obj === null) return;
// 如果是数组,修改其实例的原型
if(Array.isArray(obj)) {
// 3. 将得到的新的原型设置到数组实例原型上
obj.__proto__ = arrayProto
// 4. 对数组内的元素,同样进行响应化
for(let i = 0; i < obj.length; i++) {
// console.log(obj[i]);
observer(obj[i])
}
// 如果是对象
}else {
Object.keys(obj).forEach(key => {
console.log(obj, key, obj[key]);
defineReactive(obj, key, obj[key])
})
}
}
obj = [{a: 1}, 2, 7, 5 ,3]
observer(obj)
obj.push(4) // 数组正在执行push方法
obj.pop() // 数组正在执行pop方法
obj[0].a = 2
// 获取a
// 正在改变a值:从1变为2
obj.sort() // 数组正在执行sort方法
console.log(obj); // [ 2, 3, 5, 7, { a: [Getter/Setter] } ]
console.log(obj[4].a);
// 获取a
// 2