vue数据驱动模拟实现
Vue 数据驱动模拟实现
Vue 的数据驱动核心基于响应式系统,通过劫持数据变化触发视图更新。以下是模拟实现的关键步骤:
响应式数据劫持
使用 Object.defineProperty 或 Proxy 监听对象属性变化:
function defineReactive(obj, key, val) {
const dep = new Dep(); // 依赖收集器
Object.defineProperty(obj, key, {
get() {
Dep.target && dep.addSub(Dep.target); // 收集依赖
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify(); // 触发更新
}
});
}
依赖收集与派发更新
实现 Dep(依赖管理器)和 Watcher(观察者):
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
class Watcher {
constructor(vm, key, cb) {
Dep.target = this;
this.vm = vm;
this.key = key;
this.cb = cb;
this.value = vm[key]; // 触发 getter 收集依赖
Dep.target = null;
}
update() {
const newValue = this.vm[this.key];
if (newValue !== this.value) {
this.cb(newValue);
this.value = newValue;
}
}
}
模板编译与绑定
模拟 compile 解析模板并绑定数据:
function compile(el, vm) {
const fragment = document.createDocumentFragment();
let child;
while ((child = el.firstChild)) {
fragment.appendChild(child);
}
const nodes = fragment.querySelectorAll('[v-model]');
nodes.forEach(node => {
const key = node.getAttribute('v-model');
new Watcher(vm, key, value => {
node.value = value;
});
node.addEventListener('input', e => {
vm[key] = e.target.value;
});
});
el.appendChild(fragment);
}
完整示例整合
class MiniVue {
constructor(options) {
this.$options = options;
this.$data = options.data;
this.observe(this.$data);
compile(options.el, this);
}
observe(data) {
Object.keys(data).forEach(key => {
defineReactive(this, key, data[key]);
});
}
}
使用示例
<div id="app">
<input v-model="message" />
<p>{{ message }}</p>
</div>
<script>
const vm = new MiniVue({
el: '#app',
data: { message: 'Hello Vue' }
});
</script>
关键点说明
- 数据劫持:通过拦截对象的读写操作实现响应式。
- 依赖收集:在
getter中收集依赖,在setter中触发更新。 - 异步更新队列:实际 Vue 会使用队列优化多次更新(此处简化实现)。
- 虚拟 DOM 差异:完整版 Vue 会结合虚拟 DOM 提升性能,模拟实现可省略此步骤。







