Vue.js 的双向绑定是通过 数据劫持(Data Observation) 和 发布-订阅模式(Publish-Subscribe Pattern) 实现的。具体来说,Vue 使用了以下核心技术:
-
数据劫持:通过
Object.defineProperty
或Proxy
监听数据的变化。 -
依赖收集:在数据读取时收集依赖(Watcher)。
-
派发更新:在数据变化时通知依赖更新。
以下是 Vue 双向绑定的实现原理和详细步骤:
1. 数据劫持
Vue 通过数据劫持监听数据的变化。在 Vue 2.x 中,使用 Object.defineProperty
;在 Vue 3.x 中,使用 Proxy
。
(1)Vue 2.x:Object.defineProperty
-
原理:通过
Object.defineProperty
将对象的属性转换为getter
和setter
。 -
示例:
function defineReactive(obj, key, val) {Object.defineProperty(obj, key, {get() {console.log(`读取 ${key}: ${val}`);return val;},set(newVal) {if (newVal !== val) {console.log(`设置 ${key}: ${newVal}`);val = newVal;}},});
}const data = {};
defineReactive(data, 'message', 'Hello');
console.log(data.message); // 读取 message: Hello
data.message = 'World'; // 设置 message: World
(2)Vue 3.x:Proxy
-
原理:通过
Proxy
监听整个对象的变化,支持动态添加属性。 -
示例:
function reactive(obj) {return new Proxy(obj, {get(target, key) {console.log(`读取 ${key}: ${target[key]}`);return target[key];},set(target, key, newVal) {if (target[key] !== newVal) {console.log(`设置 ${key}: ${newVal}`);target[key] = newVal;}return true;},});
}const data = reactive({ message: 'Hello' });
console.log(data.message); // 读取 message: Hello
data.message = 'World'; // 设置 message: World
2. 依赖收集
Vue 在数据读取时收集依赖(Watcher),在数据变化时通知依赖更新。
(1)Watcher
-
作用:Watcher 是 Vue 中的观察者,负责监听数据的变化并执行回调。
-
示例:
class Watcher {constructor(vm, key, updateFn) {this.vm = vm;this.key = key;this.updateFn = updateFn;// 触发 getter,收集依赖Dep.target = this;this.vm[this.key];Dep.target = null;}update() {this.updateFn.call(this.vm, this.vm[this.key]);}
}
(2)Dep
-
作用:Dep 是依赖管理器,负责存储 Watcher 并通知更新。
-
示例:
class Dep {constructor() {this.subscribers = [];}depend() {if (Dep.target) {this.subscribers.push(Dep.target);}}notify() {this.subscribers.forEach(sub => sub.update());}
}
(3)结合数据劫持和依赖收集
-
在
getter
中收集依赖,在setter
中派发更新。 -
示例:
function defineReactive(obj, key, val) {const dep = new Dep();Object.defineProperty(obj, key, {get() {if (Dep.target) {dep.depend(); // 收集依赖}return val;},set(newVal) {if (newVal !== val) {val = newVal;dep.notify(); // 派发更新}},});
}
3. 派发更新
当数据变化时,Vue 会通知所有依赖(Watcher)更新视图。
示例:
const data = {};
defineReactive(data, 'message', 'Hello');new Watcher(data, 'message', (value) => {console.log(`视图更新: ${value}`);
});data.message = 'World'; // 设置 message: World -> 视图更新: World
4. 双向绑定的实现
Vue 的双向绑定是通过 v-model
指令实现的。v-model
是 v-bind
和 v-on
的语法糖。
(1)v-model
的原理
-
v-bind
:将数据绑定到视图。 -
v-on
:监听视图的变化并更新数据。
(2)示例
<input v-model="message" />
等价于:
<input :value="message" @input="message = $event.target.value" />
5. Vue 3.x 的改进
在 Vue 3.x 中,使用 Proxy
替代 Object.defineProperty
,解决了以下问题:
-
动态添加属性:
Proxy
可以监听动态添加的属性。 -
性能优化:
Proxy
的性能优于Object.defineProperty
。
6. 总结
Vue 的双向绑定是通过以下步骤实现的:
-
数据劫持:使用
Object.defineProperty
或Proxy
监听数据的变化。 -
依赖收集:在数据读取时收集依赖(Watcher)。
-
派发更新:在数据变化时通知依赖更新。
-
双向绑定:通过
v-model
实现视图和数据的双向同步。
通过这种机制,Vue 实现了高效的双向绑定,使得开发者可以专注于业务逻辑,而无需手动操作 DOM。