网络上有很多教你如何模仿vue手写mvvm的视频,我从来没有自己动手写过。这周末想着一定要把这件事情完成。
下面就关于vue如何实现two-way data-binding做一个简单的步骤梳理,文字有点多,不过逐步看下去或许会对大家有所启发哦,同时我也会把需要用到的知识点罗列出来~
所谓双向绑定,即数据变化 -> 视图更新;视图交互变化(如input) -> 数据model变更的效果。
何为mvvm?简言之,把数据和视图进行关联的一种模式。这四个字母可以这么理解: Model, View, ViewModel。
模仿vue来写一个自己的mvvm,往简单来讲,一般涉及下面三个步骤:
- compile — 模板编译
- observe — 数据劫持
- watcher — 监听model的变化,并告知视图重新编译,是连接compile和observe的桥梁
模板编译 Compile
- 用过vue的人都知道,html中有各种v-开头的指令,也有类似mustache的模板。这部分内容都是要用自定义的data数据进行绑定的。
因此,模板的编译就是把你自定义的无论是v-(还是k-,你随意~)开头的指令,还是mustache,用你想要的data去替换。这是编译模板最开始需要想到的。 - 如何进行编译,我们都知道频繁的操作dom是导致性能低下的一大原因,相对于拼接字符串无法操作dom节点来讲,把dom放到内存中操作可以大大提升速度,这就涉及document.createDocumentFragment方法。
另外,还会要用到诸如appendChild, childNodes, node.attributes, node.textContent, node.nodeType, firstChild等原生js操作dom的方法。
由于处理的dom集合都是类数组,所以一个频繁涉及到的原生js方法是Array.from,把类数组转化成数组;还有一个高频方法的是Array.prototype.reduce。
还需要特别掌握的是,递归在项目中的运用。
在具体处理的代码中就会考察你的基础知识啦。所以,光会用vue、react、angular啥的不行哦,就像你光知道这个机器怎么使用,一旦停电了或机器故障了,你就只能两手一摊了。 - 在内存中处理好之后,再把fragment放回到页面中即可。至此,页面最初始的模板编译完成。
数据劫持 Observe
所谓数据劫持就是当数据(一定是对象,引用类型的值)发生改变的时候,添加我们自己的逻辑。一般情况下,我们给对象赋值都是直接let obj = value,完毕。在这个过程中我们无法添加自己的逻辑,比如你希望当对象的值发生改变时(即监听对象的变化,当发生改变时调用方法),例如给一个console输出或做点别的,却无法做到。
那么如何添加自己的逻辑呢?答案就是ES5的Object.defineProperty方法。在调用Object.defineProperty方法时,如果不指定,configurable,enumerable和writable特性的默认值都是false,所以务必要把configurable和enumerable的值设置成true。最关键的是,给对像的某个key写上get和set方法,这样一来你就可以在取值和赋值的过程中添加自己的逻辑了。
监听(数据)变化 Watcher
这是难点,也是关键点。
在Observe观察到对象数据发生变化了,但是无法改变视图(view)。所以,watcher的作用,就是给各个绑定数据的地方,一一对应的进行监听。在Observe发现数据变化后,调用watcher函数对相应的视图进行数据更新。
未完待续~
ps.个人认为单个功能的代码不超过30行比较好。如果超过了,可以拆分成多个细小的代码块,方便阅读,也方便梳理逻辑。