vue双向绑定代码实现
Vue 双向绑定的实现原理
Vue 的双向绑定主要通过数据劫持结合发布者-订阅者模式实现。核心是利用 Object.defineProperty(Vue 2.x)或 Proxy(Vue 3.x)来监听数据变化,并通过事件机制通知视图更新。
Vue 2.x 实现代码示例
数据劫持与监听
function defineReactive(obj, key, val) {
const dep = new Dep(); // 依赖收集器
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.addSub(Dep.target); // 收集当前依赖
}
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify(); // 通知所有订阅者更新
}
});
}
依赖收集器(Dep)
class Dep {
constructor() {
this.subs = []; // 订阅者列表
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
Dep.target = null; // 静态属性,指向当前正在处理的 Watcher
订阅者(Watcher)

class Watcher {
constructor(vm, exp, cb) {
this.vm = vm;
this.exp = exp;
this.cb = cb;
this.value = this.get(); // 触发依赖收集
}
get() {
Dep.target = this;
const value = this.vm.data[this.exp]; // 触发 getter
Dep.target = null;
return value;
}
update() {
const newValue = this.vm.data[this.exp];
if (newValue !== this.value) {
this.value = newValue;
this.cb.call(this.vm, newValue);
}
}
}
编译器(Compiler)
function compile(node, vm) {
if (node.nodeType === 1) { // 元素节点
const attrs = node.attributes;
for (let i = 0; i < attrs.length; i++) {
if (attrs[i].nodeName === 'v-model') {
const exp = attrs[i].nodeValue;
node.addEventListener('input', e => {
vm.data[exp] = e.target.value;
});
new Watcher(vm, exp, value => {
node.value = value;
});
}
}
}
}
Vue 3.x 实现代码示例(基于 Proxy)
响应式数据

function reactive(obj) {
const handlers = {
get(target, key, receiver) {
track(target, key); // 依赖收集
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
Reflect.set(target, key, value, receiver);
trigger(target, key); // 触发更新
return true;
}
};
return new Proxy(obj, handlers);
}
const targetMap = new WeakMap(); // 存储依赖关系
function track(target, key) {
if (!activeEffect) return;
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
dep.add(activeEffect);
}
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const effects = depsMap.get(key);
effects && effects.forEach(effect => effect());
}
副作用函数(Effect)
let activeEffect = null;
function effect(fn) {
activeEffect = fn;
fn();
activeEffect = null;
}
双向绑定示例
HTML 模板
<input id="input" type="text" v-model="message">
<div id="output"></div>
初始化 Vue 实例
const vm = {
data: reactive({ message: '' }),
mount() {
effect(() => {
document.getElementById('output').textContent = vm.data.message;
});
document.getElementById('input').addEventListener('input', e => {
vm.data.message = e.target.value;
});
}
};
vm.mount();
关键点总结
- Vue 2.x 使用
Object.defineProperty监听对象属性,需递归遍历对象并劫持每个属性。 - Vue 3.x 使用
Proxy直接代理整个对象,无需递归且能监听新增/删除属性。 - 依赖收集通过
Dep和Watcher(Vue 2.x)或effect(Vue 3.x)实现。 - 视图更新通过事件监听(如
input)和响应式数据变更触发。






