### Vue 响应式原理详解
Vue 的响应式机制基于 JavaScript 中的 `Object.defineProperty` 方法来实现数据劫持,同时结合观察者模式和发布/订阅模式完成视图与模型之间的同步。以下是对其工作流程的具体分析:
#### 1. **数据劫持**
Vue 使用 `Object.defineProperty` 对象的属性进行拦截操作,将其转化为 getter 和 setter。每当访问或修改这些属性时,都会触发相应的逻辑。
```javascript
function defineReactive(obj, key, val) {
const dep = new Dep(); // 创建依赖收集器
observe(val); // 递归观测嵌套对象
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
if (Dep.target) { // 判断是否有 watcher 正在追踪此依赖
dep.depend();
}
return val;
},
set: function reactiveSetter(newVal) {
if (newVal === val) return;
val = newVal; // 更新值
observe(newVal); // 新值可能是对象,继续观测
dep.notify(); // 通知所有订阅者更新
}
});
}
```
这段代码展示了如何通过定义 getter 和 setter 来监控属性的变化[^3]。
---
#### 2. **依赖收集**
当组件初始化时,模板中的表达式会触发相应数据的 getter 函数,此时 Vue 将当前的 watcher 添加到对应的依赖列表中。这种行为被称为“依赖收集”。
```javascript
class Dep {
constructor() {
this.subs = []; // 订阅者列表
}
depend() {
if (Dep.target) {
this.addSub(Dep.target);
}
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
// 当前正在运行的 watcher
Dep.target = null;
class Watcher {
constructor(vm, exprOrFn, cb) {
this.vm = vm;
this.exprOrFn = exprOrFn;
this.cb = cb;
Dep.target = this; // 设置当前 watcher
this.get(); // 初始化时触发 getter 收集依赖
Dep.target = null; // 清除 target
}
update() {
this.run();
}
run() {
const newValue = this.getVmValue(this.vm, this.exprOrFn);
if (newValue !== this.value) {
this.value = newValue;
this.cb.call(this.vm, newValue);
}
}
getVmValue(vm, exprOrFn) {
return typeof exprOrFn === 'function' ? exprOrFn.call(vm, vm) : vm[exprOrFn];
}
}
```
以上代码片段说明了依赖是如何被收集以及后续的通知过程[^4]。
---
#### 3. **数组的特殊处理**
由于原生数组方法不会触发 getter 或 setter,因此 Vue 需要对数组方法进行重写以支持响应式功能。具体来说,Vue 替换了部分数组原型上的方法(如 push、pop 等),使其能够重新触发依赖更新。
```javascript
const arrayProto = Array.prototype;
export const arrayMethods = Object.create(arrayProto);
[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
].forEach(methodName => {
const originalMethod = arrayProto[methodName];
def(arrayMethods, methodName, function mutator(...args) {
const result = originalMethod.apply(this, args);
const ob = this.__ob__;
let inserted;
switch (methodName) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.slice(2);
break;
}
if (inserted) ob.observeArray(inserted);
ob.dep.notify();
return result;
});
});
```
这部分代码实现了对数组变异方法的支持[^3]。
---
#### 4. **整体流程总结**

- **数据绑定阶段**:Vue 实例化过程中,将 data 转换为响应式对象。
- **模板编译阶段**:解析 DOM 结构并将其中的数据映射至响应式系统的 getter 上。
- **渲染阶段**:当用户交互引起数据变化时,setter 自动触发更新逻辑,最终反映到页面上。
---
### 示例代码
以下是一个简单的 Vue 响应式模拟实现:
```javascript
function observer(data) {
if (!data || typeof data !== 'object') return;
Object.keys(data).forEach(key => {
defineReactive(data, key, data[key]);
});
}
observer({
msg: 'Hello World!',
count: 0
});
function defineReactive(target, key, value) {
const dep = new Set(); // 存储依赖关系
observer(value); // 深度遍历子属性
Object.defineProperty(target, key, {
get() {
if (Dep.target) {
dep.add(Dep.target);
}
return value;
},
set(newValue) {
if (newValue === value) return;
value = newValue;
observer(newValue); // 如果新值是对象,则继续观测
dep.forEach(watcher => watcher.update()); // 通知所有依赖更新
}
});
}
```
---
###