基于vue 看mvvm 框架设计基本思路
MVVM(model<->viewmodel<->view)
数据双向绑定(data-binding)是mvvm的核心思想,View 和 Model 之间没有联系,通过 ViewModel 进行交互。用户操作 View,ViewModel 感知到变化,通知 Model 发生相应改变;反之当 Model 发生改变,ViewModel 也能感知到变化,使 View 作出相应更新。
vue mvvm基本流程
模板引擎 生成render函数(首次渲染), 响应式 (reactive data)监听数据改变,触发 渲染(render函数调用)
1.通过模板生成render函数
<div id='app'> <p>{{age}}</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script type="text/javascript"> var vm = new Vue({ el:'#app', data: { age:18 } }) </script> //此模板生成的render函数体如下: with(this) {//this即vm return _c( 'div', { attrs:{"id":"app"} }, [ _c('p',[_v(_s(age))])//age即this.age,即vm.age,即data中的age;_c 即 this._c,即vm._c ] ) }
- _ c:创建html标签的方法。类似createElement,生成的是vnode,
- - _v:创建文本的vnode。对应源码中的方法createTextVNode
- - _s:转字符串的方法
生成html
render 函数生成的是vnode,vnode 转html, 参考snabbdom中的patch
响应式
通过es6 proxy 拦截 set和get方法实现监听,然后在set方法 触发trigger 实现re-render。
//多层递归监听
const defaultData = { a1: { a2: 1, }, }; function createGetter(data){ return new Proxy(data, { get: function(target, prop, receiver) { const res = Reflect.get(target, prop, receiver); console.log('get', res); if (typeof a === 'object') { return createGetter(res); } return res; }, set: function(target, prop, value, receiver) { console.log('set', prop, value); // 缺省行为时返回默认操作 return Reflect.set(target, prop, value, receiver); }, }); } const obj = createGetter(defaultData); obj.a1.a2 = 2; // get {a2: 1} // set a2 2
Virtual DOM
Virtual DOM是用JavaScript模拟DOM结构
<ul id='list'> <li class='item'>Item 1</li> <li class='item'>Item 2</li> <li class='item'>Item 3</li> </ul> //对应的JavaScript写法 var element = { tagName: 'ul', // 节点标签名 props: { // DOM的属性,用一个对象存储键值对 id: 'list' }, children: [ // 该节点的子节点 {tagName: 'li', props: {class: 'item'}, children: ["Item 1"]}, {tagName: 'li', props: {class: 'item'}, children: ["Item 2"]}, {tagName: 'li', props: {class: 'item'}, children: ["Item 3"]}, ] }
Diff算法
diff是Linux的基础命令,git中也有,是一个独立的算法。Virtual DOM里面用到了diff算法比较节点更新
最开始经典的深度优先遍历DFS算法,其复杂度为O(n^3),存在高昂的diff成本,然后是cito.js的横空出世,它对今后所有虚拟DOM的算法都有重大影响。它采用两端同时进行比较的算法,将diff速度拉高到几个层次。紧随其后的是kivi.js,在cito.js的基出提出两项优化方案,使用key实现移动追踪及基于key的编辑长度距离算法应用(算法复杂度 为O(n^2))。但这样的diff算法太过复杂了,于是后来者snabbdom将kivi.js进行简化,去掉编辑长度距离算法,调整两端比较算法。速度略有损失,但可读性大大提高。再之后,就是著名的vue2.0 把snabbdom整个库整合掉了。