vue数据双向绑定实现
Vue 数据双向绑定的实现原理
Vue 的数据双向绑定主要通过 v-model 指令实现,其核心是基于数据劫持和发布-订阅模式。以下是关键实现机制:
数据劫持(响应式系统)
Vue 使用 Object.defineProperty(Vue 2)或 Proxy(Vue 3)劫持数据的读写操作,在属性被访问或修改时触发更新。
Vue 2 实现示例:
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}`);
return val;
},
set(newVal) {
if (newVal !== val) {
console.log(`更新 ${key} 为 ${newVal}`);
val = newVal;
// 触发视图更新
}
}
});
}
Vue 3 使用 Proxy:
const reactive = (target) => {
return new Proxy(target, {
get(target, key) {
console.log(`读取 ${key}`);
return Reflect.get(target, key);
},
set(target, key, value) {
if (target[key] !== value) {
console.log(`更新 ${key} 为 ${value}`);
Reflect.set(target, key, value);
// 触发视图更新
}
return true;
}
});
};
依赖收集与派发更新
-
依赖收集
在 getter 中,Vue 会将当前正在计算的 Watcher(依赖)添加到 Dep(依赖管理器)中。 -
派发更新
在 setter 中,Dep 会通知所有 Watcher 执行更新,触发组件重新渲染。
简化版 Dep 和 Watcher:
class Dep {
constructor() {
this.subscribers = [];
}
depend() {
if (Dep.target) {
this.subscribers.push(Dep.target);
}
}
notify() {
this.subscribers.forEach(watcher => watcher.update());
}
}
class Watcher {
constructor(updateFn) {
this.updateFn = updateFn;
Dep.target = this;
this.updateFn(); // 触发 getter,收集依赖
Dep.target = null;
}
update() {
this.updateFn();
}
}
v-model 的实现
v-model 是语法糖,本质上是 :value 和 @input 的组合:
<input v-model="message">
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value">
对于自定义组件,v-model 可通过 model 选项(Vue 2)或 v-model 参数(Vue 3)配置:
Vue 2 自定义组件:
export default {
model: {
prop: 'value',
event: 'input'
},
props: ['value'],
methods: {
updateValue(newVal) {
this.$emit('input', newVal);
}
}
}
Vue 3 自定义组件:
<CustomComponent v-model:title="pageTitle" />
<!-- 子组件通过 props 和 emit 实现 -->
<script setup>
defineProps(['title']);
defineEmits(['update:title']);
</script>
总结
- 数据劫持:通过
Object.defineProperty或Proxy监听数据变化。 - 依赖管理:在 getter 中收集依赖,在 setter 中触发更新。
- 指令解析:
v-model将表单输入与数据动态绑定,通过事件监听和属性绑定实现双向同步。







