亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

JavaScript數據綁定實現一個簡單的 MVVM 庫

2019-11-20 10:16:51
字體:
來源:轉載
供稿:網友

推薦閱讀:

實現非常簡單的js雙向數據綁定

MVVM 是 Web 前端一種非常流行的開發模式,利用 MVVM 可以使我們的代碼更專注于處理業務邏輯而不是去關心 DOM 操作。目前著名的 MVVM 框架有 vue, avalon , react 等,這些框架各有千秋,但是實現的思想大致上是相同的:數據綁定 + 視圖刷新。出于好奇和一顆愿意折騰的心,我自己也沿著這個方向寫了一個最簡單的 MVVM 庫 ( mvvm.js ),總共 2000 多行代碼,指令的命名和用法與 vue 相似,在這里分享一下實現的原理以及我的代碼組織思路。

思路整理

MVVM 在概念上是真正將視圖與數據邏輯分離的模式,ViewModel 是整個模式的重點。要實現 ViewModel 就需要將數據模型(Model)和視圖(View)關聯起來,整個實現思路可以簡單的總結成 5 點:

實現一個 Compiler 對元素的每個節點進行指令的掃描和提取;

實現一個 Parser 去解析元素上的指令,能夠把指令的意圖通過某個刷新函數更新到 dom 上(中間可能需要一個專門負責視圖刷新的模塊)比如解析節點 <p v-show="isShow"></p> 時先取得 Model 中 isShow 的值,再根據 isShow 更改 node.style.display 從而控制元素的顯示和隱藏;

實現一個 Watcher 能將 Parser 中每條指令的刷新函數和對應 Model 的字段聯系起來;

實現一個 Observer 使得能夠對對象的所有字段進行值的變化監測,一旦發生變化時可以拿到最新的值并觸發通知回調;

利用 Observer 在 Watcher 中建立一個對 Model 的監聽 ,當 Model 中的一個值發生變化時,監聽被觸發,Watcher 拿到新值后調用在步驟 2 中關聯的那個刷新函數,就可以實現數據變化的同時刷新視圖的目的。

效果示例

首先粗看下最終的使用示例,與其他 MVVM 框架的實例化大同小異:

<div id="mobile-list"><h1 v-text="title"></h1><ul><li v-for="item in brands"><b v-text="item.name"></b><span v-show="showRank">Rank: {{item.rank}}</span></li></ul></div>var element = document.querySelector('#mobile-list');var vm = new MVVM(element, {'title' : 'Mobile List','showRank': true,'brands' : [{'name': 'Apple', 'rank': 1},{'name': 'Galaxy', 'rank': 2},{'name': 'OPPO', 'rank': 3}]});vm.set('title', 'Top 3 Mobile Rank List'); // => <h1>Top 3 Mobile Rank List</h1>

模塊劃分

我把 MVVM 分成了五個模塊去實現: 編譯模塊 Compiler 、解析模塊 Parser 、視圖刷新模塊 Updater 、數據訂閱模塊 Watcher 和 數據監聽模塊 Observer 。流程可以簡述為:Compiler 編譯好指令后將指令信息交給解析器 Parser 解析,Parser 更新初始值并向 Watcher 訂閱數據的變化,Observer 監測到數據的變化然后反饋給 Watcher ,Watcher 再將變化結果通知 Updater 找到對應的刷新函數進行視圖的刷新。

上述流程如圖所示:

下文就介紹下這五個模塊實現的基本原理(代碼只貼重點部分,完整的實現請到我的 Github 翻閱)

1. 編譯模塊 Compiler

Compiler 的職責主要是對元素的每個節點進行指令的掃描和提取。因為編譯和解析的過程會多次遍歷整個節點樹,所以為了提高編譯效率在 MVVM 構造函數內部先將 element 轉成一個文檔碎片形式的副本 fragment 編譯對象是這個文檔碎片而不應該是目標元素,待全部節點編譯完成后再將文檔碎片添加回到原來的真實節點中。

vm.complieElement 實現了對元素所有節點的掃描和指令提?。?/p>

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.childNodes.length) {this.complieElement(node, false);}}// 掃描完成,編譯所有含有指令的節點if (root) {this.compileAllNodes();}}

vm.compileAllNodes 方法將會對 this.$unCompileNodes 中的每個節點進行編譯(將指令信息交給 Parser ),編譯完一個節點后就從緩存隊列中移除它,同時檢查 this.$unCompileNodes.length 當 length === 0 時說明全部編譯完成,可以將文檔碎片追加到真實節點上了。

2. 指令解析模塊 Parser

當編譯器 Compiler 把每個節點的指令提取出來后就可以給到解析器解析了。每一個指令都有不同的解析方法,所有指令的解析方法只要做好兩件事:一是將數據值更新到視圖上(初始狀態),二是將刷新函數訂閱到 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);});}

3. 數據訂閱模塊 Watcher

上個例子,Watcher 提供了一個 watch 方法來對數據變化進行訂閱,一個參數是模型字段 model 另一個是回調函數,回調函數是要通過 Observer 來觸發的,參數傳入新值 last 和 舊值 old , Watcher 拿到新值后就可以找到 model 對應的回調(刷新函數)進行更新視圖了。model 和 刷新函數是一對多的關系,即一個 model 可以有任意多個處理它的回調函數(刷新函數),比如: v-text="title" 和 v-html="title" 兩個指令共用一個數據模型字段。

添加數據訂閱 watcher.watch 實現方式為:

watcher.watch = function(field, callback, context) {var callbacks = this.$watchCallbacks;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 的所有回調。

4. 數據監聽模塊 Observer

Observer 是整個 mvvm 實現的核心基礎,看過有一篇文章說 O.o (Object.observe) 將會引爆數據綁定革命,給前端帶來巨大影響力,不過很可惜,ES7 草案已經將 O.o 給廢棄了!目前也沒有瀏覽器支持!所幸的是還有 Object.defineProperty 通過攔截對象屬性的存取描述符(get 和 set) 可以模擬一個簡單的 Observer :

// 攔截 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 等) 該如何監測?所有的 MVVM 框架都是通過重寫該數組的原型來實現的:

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;}

這個實現方式是從 vue 中參考來的,覺得用的很妙,不過數組的 length 屬性是不能夠被監聽到的,所以在 MVVM 中應避免操作 array.length

5. 視圖刷新模塊 Updater

Updater 在五個模塊中是最簡單的,只需要負責每個指令對應的刷新函數即可。其他四個模塊經過一系列的折騰,把最后的成果交給到 Updater 進行視圖或者事件的更新,比如 v-text 的刷新函數為:

updater.updateNodeTextContent = function(node, text) {node.textContent = text;}

v-bind:style 的刷新函數:

updater.updateNodeStyle = function(node, propperty, value) {node.style[propperty] = value;}

雙向數據綁定的實現

表單元素的雙向數據綁定是 MVVM 的一個最大特點之一:

其實這個神奇的功能實現原理也很簡單,要做的只有兩件事:一是數據變化的時候更新表單值,二是反過來表單值變化的時候更新數據,這樣數據的值就和表單的值綁在了一起。

數據變化更新表單值利用前面說的 Watcher 模塊很容易就可以做到:

watcher.watch(model, function(last, old) {input.value = last;});'

表單變化更新數據只需要實時監聽表單的值得變化事件并更新數據模型對應字段即可:

var model = this.$model;input.addEventListenr('change', function() {model[field] = this.value;});‘

其他表單 radio, checkbox 和 select 都是一樣的原理。

以上,整個流程以及每個模塊的基本實現思路都講完了,第一次在社區發文章,語言表達能力不太好,如有說的不對寫的不好的地方,希望大家能夠批評指正!

結語

折騰這個簡單的 mvvm.js 是因為原來自己的框架項目中用的是 vue.js 但是只是用到了它的指令系統,一大堆功能只用到四分之一左右,就想著只是實現 data-binding 和 view-refresh 就夠了,結果沒找這樣的 javascript 庫,所以我自己就造了這么一個輪子。

雖說功能和穩定性遠不如 vue 等流行 MVVM 框架,代碼實現可能也比較粗糙,但是通過造這個輪子還是增長了很多知識的 ~ 進步在于折騰嘛!

目前我的 mvvm.js 只是實現了最本的功能,以后我會繼續完善、健壯它,如有興趣歡迎一起探討和改進~

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美日韩中国免费专区在线看| 久久久久久久电影一区| 欧美激情久久久久久| 热草久综合在线| 亚洲福利视频专区| 日韩在线www| 欧美午夜激情小视频| 欧美极品少妇xxxxⅹ喷水| 亚洲人成伊人成综合网久久久| 91精品国产色综合| 国产精品久久在线观看| 欧美国产亚洲精品久久久8v| 欧美日韩国产精品一区| 亚洲四色影视在线观看| 亚洲成年网站在线观看| 国产精品99久久久久久白浆小说| 欧美极品少妇xxxxⅹ裸体艺术| 日韩电影在线观看中文字幕| 中文字幕亚洲无线码a| 中国china体内裑精亚洲片| 在线播放国产一区中文字幕剧情欧美| 欧美精品videos另类日本| 精品动漫一区二区三区| 欧美亚洲免费电影| 久久久精品视频在线观看| 国产精品福利网站| 欧美丝袜美女中出在线| 91亚洲精品在线观看| 精品国产一区av| 国产精品高清在线观看| 国产日韩欧美黄色| 欧美午夜宅男影院在线观看| 国产精品嫩草视频| 97在线免费视频| 日韩视频欧美视频| 亚洲精品自拍视频| 国产精品v片在线观看不卡| 在线看国产精品| 在线看日韩av| 亚洲免费电影在线观看| 日韩成人高清在线| 91色琪琪电影亚洲精品久久| 一区二区三区回区在观看免费视频| 最近2019中文免费高清视频观看www99| 国产精品久久久久久搜索| 亚洲欧美综合精品久久成人| 久久综合五月天| 色www亚洲国产张柏芝| 亚洲精品国产综合久久| 亚洲美女激情视频| 激情亚洲一区二区三区四区| 久久久噜噜噜久噜久久| 日韩黄色在线免费观看| 久久大大胆人体| 久久久久久美女| 国产成人精品一区二区三区| 57pao成人国产永久免费| 国产激情999| 亚洲欧美国产日韩中文字幕| 亚洲新中文字幕| 日韩视频中文字幕| 国产精品久久久久久久一区探花| 欧美一级免费视频| 国产精品久久久久久久久久久久| 亚洲成人精品视频| 国产精品久久久久av| 欧美成人性色生活仑片| 国产在线观看精品一区二区三区| 美女视频黄免费的亚洲男人天堂| 欧美成人久久久| 国产成人一区二区| 91av网站在线播放| 欧美日本在线视频中文字字幕| 日本道色综合久久影院| 欧美成人黄色小视频| 97视频网站入口| 91久久精品久久国产性色也91| 欧美中在线观看| 国产一区二区三区在线免费观看| 国产精欧美一区二区三区| 日韩中文在线不卡| 亚洲黄在线观看| 亚洲天堂av网| 欧美香蕉大胸在线视频观看| 国产精品视频中文字幕91| 亚洲国产天堂久久综合| 午夜精品一区二区三区在线视| 国产日韩欧美在线看| 亚洲国产欧美一区二区丝袜黑人| 欧美小视频在线| 亚洲石原莉奈一区二区在线观看| 亚洲色图美腿丝袜| 亚洲男人天堂网站| 国产亚洲精品一区二555| 精品中文字幕视频| 最近2019中文字幕在线高清| 中文字幕亚洲国产| 日本精品久久中文字幕佐佐木| 国产精品久久久久久中文字| 亚洲国产精品人久久电影| 国产一区二区三区在线看| 精品香蕉在线观看视频一| 日韩在线观看免费高清完整版| 欧亚精品中文字幕| 91视频-88av| 欧美日韩高清在线观看| 疯狂做受xxxx高潮欧美日本| 国产成人一区二区三区电影| 欧美大片网站在线观看| 日韩在线免费视频观看| 日韩在线观看免费高清| 九九精品在线观看| 久久精品电影网| 最新中文字幕亚洲| 欧美精品一区三区| 日韩一二三在线视频播| 91精品久久久久久久久久入口| 亚洲在线视频福利| 久久久精品久久久久| 国产精品jizz在线观看麻豆| 久久久久久国产精品三级玉女聊斋| 日韩在线观看网址| 68精品久久久久久欧美| 亚洲色无码播放| 欧美成人小视频| 久久伊人91精品综合网站| 日韩亚洲欧美成人| 国产不卡精品视男人的天堂| 欲色天天网综合久久| 亚洲视频专区在线| 米奇精品一区二区三区在线观看| 日韩精品免费在线观看| 欧美午夜www高清视频| 日韩视频免费观看| 亚洲黄页网在线观看| 欧美美女15p| 日韩中文字幕第一页| 亚洲精品短视频| 亚洲综合大片69999| 国产97色在线| 国产91精品高潮白浆喷水| 日本高清不卡在线| 中文字幕日韩高清| 亚洲级视频在线观看免费1级| 亚洲九九九在线观看| 日韩免费电影在线观看| 国产精品夜间视频香蕉| 日韩精品免费在线| 91亚洲va在线va天堂va国| 国产欧美最新羞羞视频在线观看| 亚洲韩国日本中文字幕| 国产日韩欧美成人| 揄拍成人国产精品视频| 精品久久香蕉国产线看观看gif| 国产成人精品午夜| 精品女厕一区二区三区| 一区三区二区视频| 韩国日本不卡在线| 久久夜精品香蕉| 欧美精品videossex性护士| 日韩美女视频免费在线观看| 亚洲国产精品人久久电影| 岛国av午夜精品| 色偷偷偷综合中文字幕;dd|