vue双向实现
Vue 双向数据绑定的实现原理
Vue 的双向数据绑定主要通过 v-model 指令实现,其核心是基于数据劫持(Object.defineProperty 或 Proxy)和发布-订阅模式。以下是具体实现方式:
数据劫持(响应式系统)
Vue 通过劫持数据的 getter 和 setter 实现响应式。当数据被访问或修改时,触发依赖收集和更新通知。
// 简化版数据劫持实现
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}`);
return val;
},
set(newVal) {
if (newVal === val) return;
console.log(`更新 ${key}`);
val = newVal;
// 触发视图更新(实际会通知依赖的 Watcher)
}
});
}
v-model 的实现
v-model 是语法糖,本质上是 :value 绑定和 @input 事件的组合:
<input v-model="message">
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value">
基于 Proxy 的实现(Vue 3)
Vue 3 使用 Proxy 替代 Object.defineProperty,解决了深层对象监听和数组限制的问题:
const reactive = (target) => {
return new Proxy(target, {
get(target, key) {
track(target, key); // 依赖收集
return Reflect.get(target, key);
},
set(target, key, value) {
Reflect.set(target, key, value);
trigger(target, key); // 触发更新
return true;
}
});
};
手动实现简易双向绑定
以下是一个脱离 Vue 环境的原生实现示例:
<input id="input" type="text">
<p id="display"></p>
<script>
const data = { value: '' };
const input = document.getElementById('input');
const display = document.getElementById('display');
// 数据劫持
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;
});
// 数据 -> 视图(初始化)
data.value = '初始值';
</script>
关键点总结
- 数据驱动视图:通过劫持数据的变化自动更新 DOM。
- 视图影响数据:通过监听表单元素的
input事件同步数据。 - 性能优化:Vue 实际实现中会使用异步更新队列(
nextTick)合并多次修改。
对于复杂场景,Vue 还实现了虚拟 DOM 和 Diff 算法来高效更新视图。







