响应式原理
数据代理
什么是数据代理
通过中间对象代理另一个对象中属性的读写操作。
如何使用数据代理
Vue 通过数据代理实现vm.name
访问 data 对象里的属性,原理就是 Object.defineProperty
API。
Object.defineProperty
遍历 data 将属性添加到 vm 上- 为 vm 属性指定 getter/setter 进行读写操作
let vm = new Vue({
el: "#add",
data: {
name: "zhang13pro",
age: 24,
},
});
数据劫持
什么是数据劫持
Vue 在初始化的时候会将 data option 里的数据进行 reactive,本质就是递归给每一项属性设置 setter/getter。简单的数据劫持 ✍️
let data = {
name: "zhang13pro",
age: 24,
};
new Observer(data);
function Observer(obj) {
let keys = Object.keys(obj);
keys.forEach((k) =>
Object.defineProperty(this, k, {
get() {
return obj[k];
},
set(val) {
obj[k] = val;
},
})
);
}
数据劫持的局限性
对于数组类型的数据,Vue 不会对其变更进行 setter/getter,也就是说对于形如array[0] = '新元素'
的变更,Vue 是无法数据绑定更新页面的。
Vue wraps an observed array's mutation methods so they will also trigger view updates.
对此,Vue 拦截了 Array.prototype 进行包装,重写了数组的七个修改数组的变更方法,使用它们让数组的数据绑定重新生效。
数组更改是如何实现的
拦截原始数组方法,进行额外操作,包装器模式、面向切面编程思想(AOP):
let arrayProto = Array.prototype;
// 不破坏封装的前提下,动态的扩展功能
let arrayMethods = Object.create(arrayProto); // arrayMethods.__proto__ = arrayProto
let methodsToWrap = [
"push",
"pop",
"shift",
"unshift",
"splice",
"reverse",
"sort",
];
methodsToWrap.forEach((method) => {
arrayMethods[method] = function (...args) {
// 保留原始数组方法的结果
let result = arrayProto[method].apply(this, args);
// 数据绑定
switch (method) {
case "push":
xxx;
break;
case "unshift":
xxx;
break;
case "splice":
xxx;
default:
break;
}
return result;
};
});