vue双向数据实现
Vue 双向数据绑定的实现原理
Vue 的双向数据绑定主要通过 v-model 指令实现,其核心是结合了数据劫持(Object.defineProperty 或 Proxy)和发布-订阅模式。以下是具体实现机制:
数据劫持与响应式系统
Vue 2.x 使用 Object.defineProperty 劫持对象属性的 getter 和 setter。当数据被访问或修改时,触发依赖收集和更新通知。

// 简化版数据劫持示例
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log('收集依赖');
return val;
},
set(newVal) {
if (newVal !== val) {
console.log('触发更新');
val = newVal;
}
}
});
}
Vue 3.x 改用 Proxy 代理整个对象,能直接监听对象和数组的变化,无需递归遍历属性。

模板编译与指令解析
v-model 在模板编译阶段被解析为语法糖:
- 对于表单元素(如
input),v-model自动绑定value属性和input事件:<input v-model="message"> <!-- 等价于 --> <input :value="message" @input="message = $event.target.value"> - 对于自定义组件,
v-model默认绑定valueprop 和input事件,Vue 3.x 可通过modelValue和update:modelValue自定义。
依赖收集与派发更新
- 依赖收集:在
getter中,当前组件的Watcher实例会被添加到依赖列表(Dep)。 - 派发更新:在
setter中,Dep 通知所有Watcher执行更新,触发虚拟 DOM 对比和重新渲染。
自定义实现双向绑定
以下是一个极简的双向绑定示例:
<input id="input" type="text">
<div id="display"></div>
<script>
const data = { text: '' };
const input = document.getElementById('input');
const display = document.getElementById('display');
// 数据劫持
Object.defineProperty(data, 'text', {
set(value) {
input.value = value;
display.innerHTML = value;
}
});
// 监听输入事件
input.addEventListener('input', (e) => {
data.text = e.target.value;
});
</script>
注意事项
- Vue 2.x 中无法检测到对象属性的添加或删除,需使用
Vue.set/this.$set。 - Vue 3.x 的
Proxy解决了这一问题,但需注意浏览器兼容性。 - 双向绑定可能带来维护成本,复杂场景建议显式使用事件通信(如
$emit)。






