如何實現一個MVVM框架
MVVM(Model View ViewModel)最初由微軟在Windows Presentation Foundation(WPF)和Silverlight中引入,近年來、它作為MVC的一種替代方案在前端也如日中天。像其他MV*一樣,MVVM中的Model代表著我們應用的資料;而View代表著使用者介面;最重要的是ViewModel,可以將其看作一個擁有雙向資料處理能力的轉換器,它將模型資料傳遞到檢視,並將檢視指令傳遞到模型。MVVM框架將前端工程師從繁瑣的DOM操作中徹底地解放出來,讓我們可以更專注於自己的業務。
接下來我們探討一種實現雙向繫結的方案,本文適合實際使用過MVVM框架的人閱讀,包括AngularJS、Avalon等。最終效果如下:
1.基本功能
雙向繫結作為MVVM框架的最大特點,是如何實現的呢?MVVM資料流示意圖如下:
示意圖中可以看出雙向資料流:
View將變動通知到ViewModel,然後ViewModel對Model進行更新。
其中最核心的功能是對檢視(View)和模型(Model)變動的監聽。
(1).檢視變動的監聽
MVVM框架都是通過相應的指令,在HTML中宣告式的標記出需要監聽的DOM節點。本文實現中,我們主要涉及到兩個指令:foio-controller
、foio-model
以及一個表示式{{}}。
比如:
<input type="text" foio-model="nickname">
上述指令foio-model,宣告將View中的input的變動通知到Model中的nickname。通過對的檢視節點(input)註冊監聽函式就可以得到檢視(input)的變動了。
//對檢視中的input節點註冊input事件監聽函式
var elem = document.querySelector('input');
if (elem.addEventListener) {
elem.addEventListener('input', callback, false);
} else {
elem.attachEvent('oninput', callback);
}
(2).模型變動的監聽
對模型變動的監聽可以通過ECMAScript5中的API實現。
Object.defineProperty(obj, prop, descriptor)
可以通過該API為物件新增一個屬性,並設定該屬性的gett函式和set函式,在訪問屬性時會觸發相應的get函式和set函式。
var air = {};
Object.defineProperty(air, 'temperature', {
get: function() {
console.log('get!');
},
set: function(value) {
console.log('set!');
}
});
air.temperature = 15; //output: set!
air.temperature; //outpu: get!
我們可以在set函式中得到模型的變動,並將相關變動通知到ViewModel。
2.總體實現
MVVM的主要流程包括(View)檢視掃描、(Model)模型構建、以及關聯檢視和模型(ViewModel)
(1)View(檢視)掃描
處理View(檢視)必然涉及到對DOM結構的掃描,通過掃描抽取指令(本文只有三種指令,foio-controller、foio-model、{{}});並對相應的節點進行如下處理:
繫結通知函式,用於在檢視更新時通知ViewModel
繫結更新函式,用於在模型更新時通過該函式更新檢視
針對不同的節點型別,這些通知函式和更新函式都是預先定義好的,儲存在directives
結構中。在節點掃描過程中,當遇到指令時,就通過executeBindings函式對相應的節點進行繫結處理。流程圖如下:
(2)Model(模型)構建
而對Model的處理也主要是註冊監聽函式,用於在Model變化時得到通知,如上圖所示。controller中的每一個變數都通過Object.defineProperty(obj, prop, descriptor)
定義到Model上,其中descriptor上的get函式可以用於蒐集依賴,而set函式則用於通知依賴於該Model的檢視進行更新。
var descriptor = {
var dependencyList = [];
get: function() {
//蒐集依賴
dependencyList.push(this);
return value;
},
set: function(newVal) {
if (oldVal === newVal) {
return;
}
oldVal = newVal;
//通知依賴於該Model的檢視進行更新
for (var dependIdx in dependencyList) {
dependencyList[dependIdx].updateView(newVal);
}
}
}
(3)關聯模型和檢視
View(檢視)掃描的結果是一個元素集合
bindings = [
{
type: type, //指令型別
element: elem, //DOM節點
expr: value, //繫結的變數名稱
},
{...}
]
而Model(模型)構建的結果也是一個集合:
vmodels = {
controller1: {
expr1: value1,
expr2: value2,
binder: {expr1: function(){},expr2:function(){}}
},
controller1: {...}
}
通過executeBindings函式,將檢視和模型關聯起來。
function executeBindings(bindings, vmodels) {
for (var i = 0, binding; (binding = bindings[i++]);) {
binding.vmodels = vmodels;
directives[binding.type](binding);
};
}
每一種指令都有不同的初始化函式,比如針對foio-model
指令,當DOM節點為input型別時,初始化函式做了三件事:
監聽input和DOMAutoComplete事件
註冊對模型的依賴
提供更新該DOM節點的方法
詳細程式碼如下:
directives['model']={
switch (binding.xtype) {
case "input":
//繫結input事件
binding.bound('input', updateVModel);
//繫結DOMAutoComplete事件
binding.bound('DOMAutoComplete', updateVModel);
//註冊對模型的依賴
elem.value = closetVmodel.binder[binding.expr].apply(binding);
//更新該DOM節點的方法
binding.updateView = function(newVal) {
elem.value = newVal;
};
break;
}
}
至此我們實現了一個基本的MVVM框架了,雖然只有三個指令,但是基本能夠說明如何設計並實現一個MVVM框架了。
本文同時發表在我的部落格積木村の研究所 :http://foio.github.io/mvvm-overview/
相關文章
- MVVM原始碼 - 如何實現一個MVVM框架MVVM原始碼框架
- 實現一個自己的mvvmMVVM
- 從0搭建一個實用的MVVM框架MVVM框架
- 基於vue實現一個簡單的MVVM框架(原始碼分析)VueMVVM框架原始碼
- 實現一個簡單的MVVM(Compile)MVVMCompile
- 如何實現一個圖片載入框架框架
- 開源 | 如何實現一個iOS AOP框架?iOS框架
- 試著用Proxy 實現一個簡單mvvmMVVM
- 一個 MVC 框架以 MVVM 之「魂」復活了!MVC框架MVVM
- 一個wpf專案的搭建prism框架mvvm框架MVVM
- ResponderChain+Strategy+MVVM實現一個優雅的TableViewAIMVVMView
- 從零到一實現類vue2.*的簡單MVVM框架VueMVVM框架
- MVVM框架的搭建(一)——背景MVVM框架
- Vue.js 是如何實現 MVVM 的?Vue.jsMVVM
- 如何實現一個TCC分散式事務框架的一點思考分散式框架
- 面試失敗貼之《如何動手建立一個簡單的MVVM框架》面試MVVM框架
- 我也來實現一把MVVMMVVM
- 基於ES5`defineProperty` 實現簡單的 Mvvm框架MVVM框架
- 從零實現MVVM模式的Web前端框架的雛形MVVM模式Web前端框架
- Go語言筆記[實現一個Web框架實戰]——EzWeb框架(一)Go筆記Web框架
- 自己動手實現一個EventBus框架框架
- 實現一個屬於自己的React框架(一)React框架
- 從零開始實現一個RPC框架(一)RPC框架
- Proxy實現vue MVVM實踐VueMVVM
- 用一個檔案,實現迷你 Web 框架Web框架
- 如何實現一個Interval HookHook
- 如何實現一個詞雲
- 如何實現一個 System Services?
- 10分鐘瞭解MVVM,實現簡易MVVMMVVM
- vue--實現MVVM原理VueMVVM
- Flutter 中的 MVVM 實現FlutterMVVM
- 從零開始實現一個RPC框架(四)RPC框架
- 從零開始實現一個RPC框架(零)RPC框架
- 從零開始實現一個RPC框架(二)RPC框架
- 從零開始實現一個RPC框架(五)RPC框架
- 手把手實現一個mini-Retrofit框架框架
- 從零開始實現一個RPC框架(三)RPC框架
- 實現一個併發任務執行框架框架
- 如何實現一個JSON.parseJSON