vue双向绑定实现简单
Vue 双向绑定的简单实现
双向绑定的核心是通过数据劫持结合发布-订阅模式实现。以下是简化版的实现步骤:
数据劫持(Object.defineProperty)
通过 Object.defineProperty 劫持对象属性的 get 和 set 方法,在数据变化时触发更新。
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 类)
Dep 类用于管理订阅者(Watcher),并在数据变化时通知所有订阅者。
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
Dep.target = null;
订阅者(Watcher 类)
Watcher 类负责更新视图,并在初始化时触发 get 以收集依赖。
class Watcher {
constructor(vm, key, cb) {
this.vm = vm;
this.key = key;
this.cb = cb;
Dep.target = this;
this.vm[this.key]; // 触发 getter 收集依赖
Dep.target = null;
}
update() {
this.cb.call(this.vm, this.vm[this.key]);
}
}
简单示例
将上述部分组合成一个简单的双向绑定示例:
class SimpleVue {
constructor(options) {
this._data = options.data;
this._el = document.querySelector(options.el);
this._compile(this._el);
this._observe(this._data);
}
_observe(data) {
Object.keys(data).forEach(key => {
defineReactive(data, key, data[key]);
});
}
_compile(node) {
if (node.nodeType === 1) {
Array.from(node.attributes).forEach(attr => {
if (attr.name === 'v-model') {
const key = attr.value;
node.addEventListener('input', e => {
this._data[key] = e.target.value;
});
new Watcher(this, key, val => {
node.value = val;
});
}
});
}
}
}
使用示例
<input type="text" v-model="message">
<div id="display">{{ message }}</div>
<script>
const vm = new SimpleVue({
el: '#app',
data: {
message: 'Hello Vue!'
}
});
</script>
注意事项
- 实际 Vue 的实现更复杂,包含虚拟 DOM、数组劫持、性能优化等。
- Vue 3 改用
Proxy替代Object.defineProperty,解决无法监听数组变化等问题。 - 完整实现还需处理模板编译、指令解析等逻辑。







