介绍
数据双向绑定也就是说,模型数据变化更新视图,视图变化更新模型数据。
v-model 实现的原理是采用 数据劫持 + 发布者-订阅者模式 实现。
- 监听模型数据变化可以使用Object.defineProperty()。
- 监听视图变化(这里使用input举例),可以使用node.addEventListener()
添加input事件。
效果图
HTML部分
<div id="app">
<input type="text" v-model="msg" />
</div>
JavaScript部分
// 核心类
class MyApp {
constructor({el, data}) {
this.container = document.querySelector(el);
this.data = data;
// 初始化数据
this.initData();
// 初始化视图
this.initView();
}
initData() {
const dep = new Dep();
Object.entries(this.data).forEach(([key, value]) => {
Object.defineProperty(this, key, {
configurable: true,
enumerable: true,
get() {
Dep.target && dep.addWatcher(Dep.target);
return value;
},
set(newValue) {
value = newValue;
dep.notify(); // 通知变化。
}
})
})
}
initView() {
const nodes = this.container.querySelectorAll('[v-model]'); // 获取所有绑定v-model自定义属性的Dom节点。
nodes.forEach(node => {
const key = node.getAttribute('v-model'); // 获取自定义属性绑定的值。
// 创建观察者
new Watcher(this, key, () => {
node.value = this[key];
});
node.value = this[key]; // 初始赋值。
node.addEventListener('input', e => {
this[key] = e.target.value; // 添加键入事件,如果改变则修改模型中对应数据的值。
}, false);
})
}
}
// 订阅者,因为有n个观察者所以需要订阅者统一进行管理。
class Dep {
constructor() {
this.watchers = [];
},
// 添加观察者
addWatcher(watcher) {
this.watchers.push(watcher);
}
// 数据变化通知观察者。
notify() {
this.watchers.forEach(watcher => {
watcher.update();
})
}
}
// 观察者
class Watcher {
constructor(vm, key, callback) {
this.vm = vm;
this.key = key;
this.callback = callback;
Dep.target = this;
this.value = this.vm[this.key]; // 为了初始化数据时创建对应的观察者,需要获取数据以达到调用definePropety()的get方法。
Dep.target = null;
}
// 修改视图
update() {
this.callback();
}
}
// 使用
const vm = new MyApp({
el: '#app',
data: {
msg: '123'
}
})
疑问:Dep.target = this 是什么作用,恳请大佬解答。