js实现vue双向绑定
实现 Vue 双向绑定的核心原理
Vue 的双向绑定主要通过数据劫持结合发布-订阅模式实现,核心是 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)。以下是简化版的实现思路:
数据劫持(响应式原理)
使用 Object.defineProperty 劫持对象的属性访问和修改:
function defineReactive(obj, key, val) {
const dep = new Dep(); // 依赖收集器
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.addSub(Dep.target); // 收集依赖
}
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify(); // 通知更新
}
});
}
依赖收集(发布-订阅模式)
实现一个简单的依赖收集器:
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
Dep.target = null; // 静态属性指向当前 Watcher
观察者(Watcher)
Watcher 负责更新视图:
class Watcher {
constructor(vm, exp, fn) {
this.vm = vm;
this.exp = exp;
this.fn = fn;
Dep.target = this;
this.vm[exp]; // 触发 getter 收集依赖
Dep.target = null;
}
update() {
this.fn(this.vm[this.exp]);
}
}
双向绑定实现示例
结合以上代码实现简单版双向绑定:
<input id="input" type="text">
<div id="text"></div>
const data = { text: '' };
const input = document.getElementById('input');
const text = document.getElementById('text');
// 数据劫持
defineReactive(data, 'text', data.text);
// 数据 -> 视图
new Watcher(data, 'text', val => {
text.innerText = val;
});
// 视图 -> 数据
input.addEventListener('input', e => {
data.text = e.target.value;
});
使用 Proxy 的实现(Vue 3.x 风格)
Vue 3.x 改用 Proxy 实现响应式:
function reactive(obj) {
const dep = new Dep();
return new Proxy(obj, {
get(target, key) {
if (Dep.target) dep.addSub(Dep.target);
return Reflect.get(target, key);
},
set(target, key, value) {
const result = Reflect.set(target, key, value);
dep.notify();
return result;
}
});
}
完整流程说明
- 初始化时通过数据劫持监听所有属性
- 编译模板时创建 Watcher 并触发 getter 收集依赖
- 数据变化时通过 setter 触发依赖更新
- 用户输入通过事件监听反向更新数据
这种方法实现了数据与视图的自动同步,是 Vue 响应式系统的核心设计。实际 Vue 源码中还包含虚拟 DOM、批量异步更新等优化策略。







