刀哥(朱建)
前言:mvvm模式即model-view-viewmodel模式简称,单/双项向数据绑定的实现,让前端开发者们从繁杂的dom事件中解脱出来,很方便的处理数据和ui之间的联动。本文将从vue的双向数据绑定入手,剖析mvvm库设计的核心代码与思路。
<强> 1,需求整理与分析强>
需求:
-
<李>
数据一旦改变则更新数据对应的ui
李> <李> ui改变则触发事件改变ui对应的数据李>分析:
-
<李>
通过dom节点的指令获取刷新函数,用来刷新指定的ui。
李> <李>实现一个桥接的方法,让刷新函数和需要的数据关联起来。
李> <李>监听数据变化,数据改变后通过桥接方法调用刷新函数。
李> <李> ui改变触发对应的dom事件在改变特定的数据。李><强> 2,实现思路强>
-
<李>
实现观察者,重新定义数据,为数据上每个属性增加setter, getter以监听数据的变化。
李> <李>实现编译、扫描模版模板,提取每个dom节点中的指令信息。
李> <李>实现指令,通过指令信息是实例化对应的指令实例,不同类型的指令拥有不同的刷新函数更新。
李> <李>实现观察家,让观察者的属性监听函数与指令的更新函数做一一对应,以实现数据变化后更新视图。李><强> 3模块划分强>
MVVM目前划分为观察者,编译指令,观察家四个模块。
<强> 4,数据监听模块观察者强>
通过es5规范中的object.defineProperty方式实现对数据的监听。
<强> 5,实现思路强>
递归遍历数据,将数据下面所有属性都加上设置,让方法,以实现对所有属性的拦截。
注意:对象可能含有数组属性,数组的内置有推动,流行、拼接等方法改变内部数据。
此时做法是改变数组的原型链,在原型链中增加一层自定义的推动,流行,拼接方法做拦截,这些方法里面加上我们自己的回调函数,然后在调用原生的推动,流行、拼接等方法。
<代码>导出功能defineProperty (obj,道具,val) { 如果(支持==__observe__) { 返回; } val=val | | obj[支持]; var dep=new dep (); obj。__observe__=大; var childDep=addObserve (val); Object.defineProperty (obj,道具,{ 得到:函数(){ var=Dep.target目标; 如果(目标){ dep.addSub(目标); 如果(childDep) { childDep.addSub(目标); } } 返回val; }, 设置:函数(newVal) { 如果(newVal !=val) { val=newVal; dep.notify (); } } }); }代码>
<强> 6,编译模块编译器强>
实现思路:
-
<李>
将模版模板上的dom遍历一遍,将其存入文档碎片碎片弹
李> <李>遍历的破片,通过属性获取节点的属性信息,在通过正则表达式过滤属性信息,进而拿到元素节点和文档节点的指令信息李><代码> var complieTemplate=function(节点模型){ 如果(节点。nodeType==1 | |节点。nodeType==11),,!实图书业管理系统(节点)){ paserNode(模型中,节点); 如果(nodes.hasChildNodes ()) { nodes.childNodes.forEach(节点=比;{ complieTemplate(节点模型); }) } } };代码>
<强> 7,指令模块指令强>
指令信息如:v-text, v, v模型等。
每种指令信息需要的初始化动作以及指令的刷新函数更新都可能不一样,所以我们把它抽象出来单独做一个模块。当然也有公用的如公共属性,统一的观察家实例化,解开。
更新函数则具体定义所属指令如何渲染ui,如简单的vtext指令的更新函数如下:
<代码> vt。更新=function (textContent) { this.el。textContent=textContent; };代码>
<强> 9日结构图强>
) 数
<强> 9日据订阅模块观察家强>
观察者的功能是让指令和观察者模块关联起来。初始化的时候做两件事: