## 1 mvvm意义
>[info] 以数据双向绑定为核心,实现UI层与数据层的互动mvvm效果
>[info] mvvm中的m指的是服务器后端的数据层封装
>[info] mvvm中的v指的是客户端视图层的封装
>[info] mvvm中的vm指的是数据层与视图层的数据绑定核心
>[info] 正如前面数据绑定介绍mvvm实现了视图层与数据层的同步刷新
## 2 思路整理
>[info] mvvm在概念上将视图层与数据层逻辑分离,ViewModel也就是vm是整个模式的核心,实现Vm需要将模型与视图关联起来。总共分为5点
>[info] (1) 实现一个Compiler对元素的每个节点进行指令的扫描和提取。这些指令实现View与Vm的互动
>[info] (2) 实现一个Parser解析元素中的指令,将指令的意图通过某个刷新函数更新到dom上。比如解析节点<p v-show = "isShow" > </p>后,根据Model中的isShow调用node.style.display控制元素的显示和隐藏
>[info] (3) 实现一个Watcher注册Parser对指令解析的刷新函数和对应Model的字段关联起来
>[info] (4) 实现一个Observer能够对象所有字段进行set检测,一旦发生变化可以对所有的消息订阅者Watcher进行刷新操作
>[info] (5) 通过Observer在Watcher中建立对Model的监听。当Model在的值发生变化时,监听者Watcher被触发刷新,调用步骤2中生成的刷新函数。实现视图的刷新操作
## 3 模块划分
>[info] 这里将mvvm框架实现为5个模块。
>[info] (1)编译模块Compiler。编译好指令将指令信息传递给解析器Parser处理
>[info] (2)解析模块Parser。解析指令初始化,生成对应Watcher刷新操作
>[info] (3)订阅模块Watcher。管理所有的消息接受与刷新操作的关联。
>[info] (4)监视模块Observer。监视数据的变化反馈给Watcher
>[info] (5)刷新模块Update。接受Watcher的刷新消息,进行视图刷新
![](https://box.kancloud.cn/2016-05-06_572c4de51a9a8.png)
## 4 编译模块Compiler
>[info] 编译模块实现对视图中元素的节点指令的扫描和提取。
>[info] 编译和解析的过程会多次遍历节点树,为了提高编译效率,在mvvm中将节点元素转换成文档碎片fragment编译对象。
>[info]待全部节点编译完成后将文档碎片添加回到原来的真实节点中
`vm.complieElement`实现对元素所有节点的扫描和指令提取
~~~
vm.complieElement = function(fragment,root) {
;获取孩子节点
var node , childNodes = fragment.childNodes;
;子节点扫描处理
for (var i = 0; i< childNodes.length; i++){
node = childNodes[i];
;包含指令
if(this.hasDirective(node)){
this.$unCompileNodes.push(node);
}
;扫描子节点
if(node.childeNodes.length){
this.compileElement(node,false);
}
}
;
if(root){
this.compileAllNodes();
}
}
~~~
`vm.compileAllNode()`实现对`this.$unCompileNodes`中每个节点的的编译(将指令信息交给解析模块Parser),编译完一个节点从缓存队列删除,检`this.$unCompileNodes`直到0。然后将文档碎片添加到真实节点上
## 5 解析模块 Parser
>[info] Compiler完成指令的提取,解析模块Parser对每个指令实现不同的解析方法
>[info] 指令的解析一方面将数据值更新到视图上,
>[info] 指令的解析一方面将刷新函数订阅到Model的变化监测中
~~~
;以v-text指令的解析为例
parser.parseVText = function(node,model){
;获取Model中定义的初始值
var text = this.$model[model];
;更新节点的文本
node.textContent = text;
;或者使用刷新函数
//updater.updateNodeTextContent(node,text);
;在watcher中订阅model的变化
watcher.watch(model,function(last,old){
node.textContent = last;
//刷新函数
//updater.updateNodeTextContent(node,text);
})
}
~~~
## 6 数据订阅模块Watcher
>[info] Watcher提供watch方法对数据变化进行订阅,
>[info] 一个参数是模型字段model
>[info] 另一个是回调函数,实现Observer来触发的,参数传入新值last和旧值old。
>[info] Watcher拿到新值后就可以找到model对应的回调进行更新视图。
>[info] 这里可以知道model和刷新函数是一对多的庴,一个model可以包含多个回调函数(刷新函数)。
~~~
;Watcher.watch()方法实现如下
watcher.watch = function(field,callback,context){
var callbacks = this.$watchCallbacks;
;检查vm.$model中属性的更新
if(!Object.hasOwnProperty.call(this.$model,field)){
console.warn('The field:'+field+'does not exist in model!');
return;
}
;缓存回调函数的数组
if(!callbacks[field]){
callbacks[field] = [];
}
;添加回调函数到数组
callbacks[field].push([callback,context]);
}
~~~
数据模型的field字段发生变化时,watcher缓存数组中订阅field的所有回调
## 7 数据监听模块Observer
>[info] Observer是整个mvvm实现的核心基础。
>[info] 其实现原理与机制见上面两节。
>[info] 这里使用Object.defineProperty实现对属性的get/set拦截
~~~
;实现object的prop属性的get和set方法
Object.defineProperty(object,prop,{
get:function(){
return this.getValue(object,prop);
}
set:function(newValue){
var oldValue = this.getValue(object,prop);
if(newValue !== oldValue){
this.setValue(object,newValue,prop);
this.triggerChange(prop,newValue,oldValue);
}
}
})
;实现对数组push和shift的监听
observer.rewriteArrayMethods = function(array){
var self = this;
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
var methods = 'push|pop|shift|unshift|splice|sort|reverse'.split('|');
methods.forEach(function(method){
Object.defineProperty(
arrayMethods,
method,
function(){
var i = arguments.length;
var original = arrayProto[method];
var args = new Array(i);
while(i--){
args[i] = arguments[i];
}
var result = original.apply(this,args);
self.triggerChange(this,method);
return result;
});
});
array.__proto__ = arrayMethods;
}
~~~
## 8 视图刷新模块Updater
>[info] 刷新模块Updater是最简单,负责每个指令对应的刷新函数。
>[info] 这里接受其他模块的解析结果。调用Update实现对视图或者事件的更新
~~~
;v-text的刷新函数
updater.updateNodeTextContent = function(node,text){
node.textContent = text;
}
;v-bind的刷新函数
updater.updateNodeStyle = function(node,propperty,value){
node.style[propperty] = value;
}
~~~
## 9 双向绑定实现
>[info] 双向绑定在数据变化时更新视图层,或者视图层变化时数据层也进行同步
>[info] 数据变化更新表单值可以使用Watcher模块
>[info] 表单变化更新数据只需要监听表单的事件
~~~
;数据到视图层
watcher.watch(model,function(last,old){
input.value = last;
});
;视图到数据层
var model = this.$model;
input.addEventListener('change',function(){
model[field] = this.value;
});
~~~
## 10 参考
[mvvm的简单实现](https://segmentfault.com/a/1190000004847657)
[mvvm的完整代码](https://github.com/tangbc/sugar)
- 概述
- 框架结构
- 编译入口(\entries)
- web-compiler.js(web编译)
- web-runtime.js(web运行时)
- web-runtime-wih-compiler.js(web编译运行)
- web-server-renderer.js(web服务器渲染)
- 核心实现 (\core)
- index.js(核心入口)
- config.js(核心配置)
- core\util(核心工具)
- core\observer(双向绑定)
- core\vdom(虚拟DOM)
- core\global-api(核心api)
- core\instance(核心实例)
- 模板编译(\compiler)
- compiler\parser(模板解析)
- events.js(事件解析)
- helper.js(解析助手)
- directives\ref.js(ref指令)
- optimizer.js(解析优化)
- codegen.js(渲染生成)
- index.js(模板编译入口)
- web渲染(\platforms\web)
- compiler(web编译目录)
- runtime(web运行时目录)
- server(web服务器目录)
- util(web工具目录)
- 服务器渲染(\server)
- render-stream.js(流式渲染)
- render.js(服务器渲染函数)
- create-renderer.js(创建渲染接口)
- 框架流程
- Vue初始化
- Vue视图数据绑定
- Vue数据变化刷新
- Vue视图操作刷新
- 框架工具
- 基础工具(\shared)
- 模板编译助手
- 核心实例工具
- Web渲染工具
- 基础原理
- dom
- string
- array
- function
- object
- es6
- 模块(Module)
- 类(Class)
- 函数(箭头)
- 字符串(扩展)
- 代理接口(Proxy)
- 数据绑定基础
- 数据绑定实现
- mvvm简单实现
- mvvm简单使用
- vdom算法
- vdom实现
- vue源码分析资料