vue实现双向绑定
Vue 实现双向绑定的原理
Vue 的双向绑定主要通过 v-model 指令实现,其底层依赖 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)进行数据劫持和发布-订阅模式。
基于 Object.defineProperty(Vue 2.x)
在 Vue 2.x 中,双向绑定的核心是通过 Object.defineProperty 劫持数据的 getter 和 setter,结合观察者模式实现数据与视图的同步。
// 简化版数据劫持示例
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}: ${val}`);
return val;
},
set(newVal) {
if (newVal !== val) {
console.log(`设置 ${key}: ${newVal}`);
val = newVal;
// 触发视图更新
}
}
});
}
const data = {};
defineReactive(data, 'message', 'Hello Vue');
data.message = 'Updated'; // 触发 setter
基于 Proxy(Vue 3.x)
Vue 3.x 改用 Proxy 实现响应式,解决了 Object.defineProperty 无法监听数组和对象新增属性的问题。

// 简化版 Proxy 实现
const reactive = (target) => {
return new Proxy(target, {
get(target, key) {
console.log(`读取 ${key}: ${target[key]}`);
return target[key];
},
set(target, key, value) {
if (target[key] !== value) {
console.log(`设置 ${key}: ${value}`);
target[key] = value;
// 触发视图更新
}
return true;
}
});
};
const data = reactive({ message: 'Hello Vue 3' });
data.message = 'Updated'; // 触发 set
v-model 的实现
v-model 是语法糖,本质上是 :value 和 @input 的组合。以输入框为例:
<input v-model="message" />
<!-- 等价于 -->
<input :value="message" @input="message = $event.target.value" />
自定义组件实现双向绑定
在自定义组件中,可以通过 model 选项和 $emit 实现双向绑定:

<custom-input v-model="message"></custom-input>
Vue.component('custom-input', {
props: ['value'],
model: {
prop: 'value',
event: 'input'
},
template: `
<input
:value="value"
@input="$emit('input', $event.target.value)"
/>
`
});
响应式系统的依赖收集
Vue 通过 Dep(依赖管理器)和 Watcher(观察者)实现依赖收集和派发更新:
- 依赖收集:在
getter中调用dep.depend(),将当前Watcher添加到Dep中。 - 派发更新:在
setter中调用dep.notify(),通知所有Watcher更新视图。
class Dep {
constructor() {
this.subs = [];
}
depend() {
if (Dep.target) {
this.subs.push(Dep.target);
}
}
notify() {
this.subs.forEach(watcher => watcher.update());
}
}
Dep.target = null;
数组的响应式处理
Vue 2.x 通过重写数组的变异方法(如 push、pop)实现响应式:
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift'].forEach(method => {
const original = arrayProto[method];
arrayMethods[method] = function(...args) {
const result = original.apply(this, args);
console.log(`数组方法 ${method} 被调用`);
// 触发视图更新
return result;
};
});






