实现Vue的响应式(对象观测)
什么是Vue的响应式?
Vue的响应式是指当Vue数据模型中的数据发生变化时,页面中涉及这些数据的部分会自动重新渲染并更新。Vue通过数据劫持方式实现响应式,也就是通过监听对象属性的变化来实现自动触发视图更新。
如何实现Vue的响应式?
Vue的响应式是基于Object.defineProperty()方法实现。该方法能够监听对象属性的变化,并在属性值变更时触发回调函数。
流程如下:
- 首先要创建一个Observer对象,采用递归的方式为所有属性添加getter和setter。getter负责收集依赖并返回属性值,setter则负责派发更新通知。
- 收集依赖,依赖就是指有哪些Watcher依赖于该属性。在getter中监听数据属性的访问者(Watcher),并将其添加到依赖里面去。
- 派发更新,当属性变更后,会在setter中通知对应的依赖进行更新操作。
下面通过两条简单的示例说明:
示例1:响应式对象
// 创建一个Observer对象,为person对象添加getter和setter方法
function Observer(obj) {
if(!obj || typeof obj !== 'object') {
return;
}
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
});
}
// 为对象添加getter和setter方法,来实现响应式
function defineReactive(obj, key, val) {
// 在getter中将Watcher添加到依赖数组中
const dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
if (Dep.target) {
dep.addDep(Dep.target);
}
return val;
},
set: function(newVal) {
if (newVal === val) {
return;
}
// 在setter中将值更新,并通知依赖更新
val = newVal;
dep.notify();
}
});
}
// 收集依赖
class Dep {
constructor() {
this.deps = [];
}
addDep(dep) {
this.deps.push(dep);
}
notify() {
this.deps.forEach(dep => {
dep.update();
});
}
}
// 观察者
class Watcher {
constructor(obj, key, cb) {
// 相当于Vue中的v-model指令
this.obj = obj;
this.key = key;
this.cb = cb;
this.value = this.get(); // 将自己添加到Dep.target
}
get() {
Dep.target = this;
const value = this.obj[this.key]; // 触发getter,将当前Watcher添加到依赖中
Dep.target = null;
return value;
}
update() {
this.value = this.get(); // 触发getter,更新value
this.cb.call(this.obj, this.value); // 执行回调函数,更新data
}
}
// 测试
const person = {
name: 'Tom',
age: 18
};
Observer(person);
new Watcher(person, 'name', function(newVal) {
console.log(`Name changed to ${newVal}`);
});
person.name = 'Jerry'; // 触发name属性的setter方法
示例2:响应式数组
// 为数组添加getter和setter方法,来实现响应式
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
const methodsToPatch = [
'push',
'pop',
'unshift',
'shift',
'splice',
'sort',
'reverse'
];
methodsToPatch.forEach(function (method) {
const original = arrayProto[method];
Object.defineProperty(arrayMethods, method, {
enumerable: false,
configurable: true,
writable: true,
value: function mutator(...args) {
const result = original.apply(this, args);
const ob = this.__ob__;
let inserted;
// 每当数组发生变化时,需要将新加入的值变成一个响应式对象
switch (method) {
case 'push':
inserted = args;
break;
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.slice(2);
break;
}
if (inserted) {
ob.observeArray(inserted);
}
ob.dep.notify();
return result;
}
});
});
// 创建一个Observer对象,为数组对象添加getter和setter方法
function observeArray(arr) {
Object.setPrototypeOf(arr, arrayMethods);
// 递归为数组元素添加响应式
for (let i = 0, l = arr.length; i < l; i++) {
observe(arr[i]);
}
}
// 测试
const arr = [1, 2, 3];
observeArray(arr);
new Watcher(arr, function (newVal) {
console.log(`Array changed to ${newVal}`);
});
arr.push(4); // 触发数组的push方法
总结
以上就是实现Vue的响应式(对象观测)的完整攻略。Vue采用Object.defineProperty()实现响应式数据绑定,核心思想是依靠数据劫持和发布-订阅模式来实现。
在实现过程中,需要创建Observer对象、收集依赖、派发更新、为数组添加getter和setter方法等步骤。通过上述两个示例,我们可以更加深入地理解Vue的响应式原理和实现方式,对于Vue应用的开发将有一定的帮助。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:一步一步实现Vue的响应式(对象观测) - Python技术站