MVVM 模型中的核心,一般通过 Object.defineProperty 的 get,set 方法进行数据的监听,在 get 里添加订阅者,set 里通知订阅者更新视图。在本项目采用 Proxy 来实现数据监听,好处有三:
Proxy 可以直接监听对象而非属性
Proxy 可以直接监听数组的变化
Proxy 有多达 13 种拦截方法,
而劣势是兼容性问题,且无法通过 polyfill 磨平。查阅
注意 Proxy 只会监听自身的每一个属性,如果属性是对象,则该对象不会被监听,所以需要递归监听
设置监听后,返回一个 Proxy 替代原数据对象
var proxy = new Proxy(data, { get: function(target, key, receiver) { //如果满足条件则添加订阅者 dep.addDep(curWatcher); return Reflect.get(target, key, receiver); }, set: function(target, key, value, receiver) { //如果满足条件则通知订阅者 dep.notfiy(); return Reflect.set(target, key, value, receiver); } });
Watcher
在 Complier 模块里对每一个解析后的 Parser 进行指令与数据模型直接的绑定,并触发 Observer 的 get 监听,添加订阅者(Watcher)
this._getter(this.parser.dirValue)(this.scope || this.parser.cs.$data);
当数据模型变化时,就会触发 -> Observer 的 set 监听 -> Dep 的 notfiy 方法(通知订阅者的所有订阅列表) -> 执行订阅列表所有 Watcher 的 update 方法 -> 执行对应 Parser 的 update -> 完成更新视图
Watcher 里的 set 方法用于设置双向绑定值,注意访问层级
Dep
MVVM 的订阅中心,在这里收集数据模型的每个属性的订阅列表
包含添加订阅者,通知订阅者等方法
本质是一种发布/订阅模式
class Dep { constructor() { this.dependList = []; } addDep() { this.dependList.push(dep); } notfiy() { this.dependList.forEach(item => { item.update(); }); } }
后记
目前该 mvvm 项目只实现了数据绑定和视图更新的功能,通过这个简易轮子的实现,对 dom 操作,proxy,发布订阅模式等若干基础知识都进行了再次理解,查漏补缺。同时欢迎大家一起探讨交流,后面会继续完善!