## 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的刷新消息,進行視圖刷新

## 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源碼分析資料