手写简易MVVM框架
参考视频:https://www.bilibili.com/video/BV1Ap411d7Ai?t=4815
- demo文件夹下为视频版源码(根目录下为手写练习,主要在compile里小部分改进,可忽略)
- 实现结果:v-model双向数据绑定及{{}}
- 代码地址:https://github.com/hxy941025/-MVVM-
大体思路
- 主要分为模板编译、数据劫持、观察者(包含发布订阅)三个部分
- 模板编译:获取vm实例里html模板,针对其中的字符串模板{{}}及v-指令进行“翻译”,写出其底层相应操作
- 数据劫持:遍历vm实例里data对象,为每个数据绑定setter和getter,触发get时将当前元素watcher推入订阅者数组,触发set时,如果值更新则通知订阅者更新数据
- 观察者:给数据对应的元素绑定一个观察者实例,并推入订阅者数组,当数据更新时发布订阅通知元素数据变化
具体模块
MVVM
- constructor 接收一个对象,将模板el和数据data绑定到this上,如果存在模板el,则开始进行编译,编译前开启数据劫持
- proxyData 将$data内数据直接代理到vm实例上
Compile 模板编译
- 编译元素 v指令等、编译文本 {{}}
- 接收两个参数,模板el及mvvm实例vm,将数据分别绑定到this上,需判断el是否为元素节点
- 如果能获取到元素,才开始编译
- 将真实DOM放入内存中,辅助方法:node2fragement,
- 编译,提取v-等及{{}}节点
- 将编译好的fragement放进页面DOM中
- compile 递归,遍历el所有子节点,若为元素节点则进行元素节点编译,若为文本节点则进行文本节点编译
- compileElement 编译元素节点,判断是否存在v-指令,进行相应操作
- compileText 编译文本节点,判断是否存在{{}},进行相应操作
- 编译工具CompileUtil
- 获取元素节点中的值,考虑多层嵌套对象的情况
- 获取文本节点中的值,按其表达式取值
- text 匹配到文本节点时的具体操作
- model 匹配到v-指令时的具体操作
- 更新函数:匹配到文本或元素节点后更新值的操作
Observer 数据劫持
- getter、setter、发布订阅
- 接收data,并观察
- observe,劫持观察data,若data不为对象或空,则不做任何操作,递归遍历data key值,一一劫持
- defineReactive 劫持函数,当触发get时将当前watcher推入订阅者数组,触发set时,如果值更新则通知订阅者更新数据
watcher 观察者
- 数据变化完需要影响模板,给需要变化的元素增加一个观察者,当数据变化后执行对应方法更新视图
- constructor 接收vm实例 expr 回调函数
- get 先获取一下老值保存在实例上
- update 对外暴露的方法,当数据更新时调用,获取新值与老值对比,如果发生变化则执行回调函数,将新值传进去
Dep 发布订阅
- 维护一个关联数据的订阅数组,当数据发生变化时,对订阅的数组遍历发布,使其重新获取数据
- addSub 收集订阅,推入订阅者数组
- notify 发布订阅