Vue.js 的响应式系统是它的核心特性之一,它允许开发者以声明式的方式将数据渲染到 DOM 上,并在数据变化时自动更新视图。Vue 2 中的响应式系统主要通过 Object.defineProperty
方法来实现,这一方法允许在对象上定义或修改属性的 getter/setter。接下来,我将通过代码示例和解释来阐述 Vue 2 中的响应式原理。
1. 响应式数据的基础:Object.defineProperty
Vue 2 使用 Object.defineProperty
来劫持对象属性的 getter 和 setter,以便在访问或修改这些属性时执行特定的逻辑(如依赖收集、派发更新等)。
function defineReactive(obj, key, val) {
// 递归处理子对象
observe(val);
// 创建一个依赖收集器
const dep = new Dep();
// 使用 Object.defineProperty 定义响应式属性
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
// 依赖收集
const target = Dep.target;
if (target) {
dep.depend();
if (isArray(val)) {
dependArray(val);
}
}
return val;
},
set: function reactiveSetter(newVal) {
// 新的值与旧值相同时不处理
if (newVal === val) return;
// 递归设置子对象的响应性
observe(newVal);
// 派发更新
dep.notify();
val = newVal;
}
});
}
// 依赖收集器
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (Dep.target) {
this.subscribers.add(Dep.target);
}
}
notify() {
this.subscribers.forEach(sub => sub.update());
}
}
// 全局变量,用于依赖收集
Dep.target = null;
// 观察一个对象
function observe(value) {
if (!isObject(value) || value instanceof VNode) {
return;
}
Object.keys(value).forEach(key => {
defineReactive(value, key, value[key]);
});
}
// 辅助函数
function isObject(value) {
return value !== null && typeof value === 'object';
}
2. 组件的响应式更新
在 Vue 组件中,data
里的属性会被转换成响应式数据。当这些属性在模板中被访问时,Vue 会使用 Dep.target
来标记当前正在计算的 watcher(通常是渲染 watcher)。
// 假设的组件初始化
function initComponent(component) {
const data = component.data();
observe(data);
// 假设的渲染函数
function render() {
Dep.target = new Watcher(component, function updateComponent() {
// 渲染逻辑
});
// 访问响应式数据,触发 getter
console.log(data.message);
Dep.target = null;
}
render();
}
// 简单的 Watcher 类
class Watcher {
constructor(vm, cb) {
this.cb = cb;
this.vm = vm;
this.deps = [];
this.get(); // 触发 getter,进行依赖收集
}
get() {
Dep.target = this;
// 假设的访问响应式数据
// this.vm.data.message;
Dep.target = null;
}
update() {
this.cb();
}
}
总结
上述代码展示了 Vue 2 中响应式系统的基础实现。通过 Object.defineProperty
,Vue 能够拦截对对象属性的访问和修改,从而执行依赖收集和派发更新等操作。在组件的渲染过程中,Vue 会利用 Dep.target
来标记当前正在计算的 watcher,并在访问响应式属性时通过 getter 进行依赖收集。当响应式数据变化时,setter 会被触发,进而通知所有依赖该数据的 watcher 执行更新操作,实现视图的自动更新。
虽然这个示例较为简化,但它捕捉了 Vue 2 响应式系统的核心思想和关键步骤。在实际应用中,Vue 还会处理更复杂的场景,如数组变化检测、计算属性、侦听器等。