实现Vue双向数据绑定
Vue 双向数据绑定的实现原理
Vue 的双向数据绑定是通过 v-model 指令实现的,其核心基于 数据劫持 和 发布-订阅模式。具体实现分为以下几个部分:
数据劫持(Object.defineProperty 或 Proxy)
Vue 2.x 使用 Object.defineProperty 劫持对象的属性访问和修改,Vue 3.x 改用 Proxy 实现更高效的数据监听。
Vue 2.x 示例:
const data = { value: '' };
Object.defineProperty(data, 'value', {
get() {
return this._value;
},
set(newVal) {
this._value = newVal;
console.log('数据更新了');
}
});
Vue 3.x 示例:

const data = { value: '' };
const proxy = new Proxy(data, {
get(target, key) {
return target[key];
},
set(target, key, value) {
target[key] = value;
console.log('数据更新了');
return true;
}
});
发布-订阅模式(Dep 和 Watcher)
Vue 通过 Dep(依赖收集器)和 Watcher(观察者)实现数据的动态更新。
- Dep:每个响应式属性都有一个
Dep实例,用于管理所有依赖该属性的Watcher。 - Watcher:在模板编译时创建,订阅数据变化并触发更新。
简化实现:

class Dep {
constructor() {
this.subscribers = [];
}
depend() {
if (Watcher.target) {
this.subscribers.push(Watcher.target);
}
}
notify() {
this.subscribers.forEach(watcher => watcher.update());
}
}
class Watcher {
constructor(callback) {
this.callback = callback;
Watcher.target = this;
this.callback(); // 触发 getter 收集依赖
Watcher.target = null;
}
update() {
this.callback();
}
}
v-model 的实现
v-model 是语法糖,本质上是 :value 和 @input 的组合。
示例:
<input v-model="message">
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value">
自定义实现双向绑定
以下是一个简化的双向绑定实现:
<input id="input" type="text">
<div id="display"></div>
<script>
const input = document.getElementById('input');
const display = document.getElementById('display');
let data = { value: '' };
// 数据劫持
Object.defineProperty(data, 'value', {
get() {
return this._value;
},
set(newVal) {
this._value = newVal;
display.textContent = newVal; // 更新视图
}
});
// 监听输入事件
input.addEventListener('input', (e) => {
data.value = e.target.value; // 更新数据
});
</script>
注意事项
- Vue 2.x 的
Object.defineProperty无法监听数组和对象的新增属性,需使用Vue.set。 - Vue 3.x 的
Proxy可以完美解决上述问题,但需注意浏览器兼容性。 - 双向绑定可能引发性能问题,应避免过度使用或结合计算属性优化。





