成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

如何實現(xiàn)VM框架中的數(shù)據(jù)綁定

BlackFlagBin / 3381人閱讀

摘要:作者佳杰本文原創(chuàng),轉(zhuǎn)載請注明作者及出處如何實現(xiàn)框架中的數(shù)據(jù)綁定一數(shù)據(jù)綁定概述視圖和數(shù)據(jù)之間的綁定二數(shù)據(jù)綁定目的不用手動調(diào)用方法渲染視圖,提高開發(fā)效率統(tǒng)一處理數(shù)據(jù),便于維護三數(shù)據(jù)綁定中的元素視圖說白了就是中元素的展示數(shù)據(jù)用于保存數(shù)據(jù)的引用類型

作者:佳杰

本文原創(chuàng),轉(zhuǎn)載請注明作者及出處

如何實現(xiàn)VM框架中的數(shù)據(jù)綁定 一:數(shù)據(jù)綁定概述
視圖(view)和數(shù)據(jù)(model)之間的綁定
二:數(shù)據(jù)綁定目的
不用手動調(diào)用方法渲染視圖,提高開發(fā)效率;統(tǒng)一處理數(shù)據(jù),便于維護
三:數(shù)據(jù)綁定中的元素
視圖(view):說白了就是html中dom元素的展示
數(shù)據(jù)(model):用于保存數(shù)據(jù)的引用類型
四:數(shù)據(jù)綁定分類
view > model的數(shù)據(jù)綁定:view改變,導(dǎo)致model改變
model > view的數(shù)據(jù)綁定:model改變,導(dǎo)致view改變
五:數(shù)據(jù)綁定實現(xiàn)方法
view > model的數(shù)據(jù)綁定實現(xiàn)方法
        修改dom元素(input,textarea,select)的數(shù)據(jù),導(dǎo)致model產(chǎn)生變化,
        只要給dom元素綁定change事件,觸發(fā)事件的時候修改model即可,不細講

model > view的數(shù)據(jù)綁定實現(xiàn)方法
        1.發(fā)布訂閱模式(backbone.js用到);
        2.數(shù)據(jù)劫持(vue.js用到);
        3.臟值檢查(angular.js用到);

六:model > view數(shù)據(jù)綁定demo講解 (如何實現(xiàn)數(shù)據(jù)改變,導(dǎo)致UI界面重新渲染)
簡易思路 
> 1.通過defineProperty來監(jiān)控model中的所有屬性(對每一個屬性都監(jiān)控)
> 2.編譯template生成DOM樹,同時綁定dom節(jié)點和model(例如
), defineProperty中已經(jīng)給“model.name”綁定了對應(yīng)的function, 一旦model.name改變,該funciton就操作上面這個dom節(jié)點,改變view 主要js模塊:Observer,Compile,ViewModel 1.Observer 用到了發(fā)布訂閱模式和數(shù)據(jù)監(jiān)控,defineProperty用于“監(jiān)控model", dom元素執(zhí)行"訂閱"操作,給model中 的屬性綁定function;model中屬性變化的時候,執(zhí)行"發(fā)布"這個操作,執(zhí)行之前綁定的那個function 源碼如下: var Observer = function(opts) { this.id = (opts && opts.id) ? opts.id : +new Date(); this.opts = opts; this.subs = []; //觀察者數(shù)組 /*this.subs包含了所有觀察者,每個觀察者的結(jié)構(gòu)如下: { key:"person.age.range",//這個key代表model.person.age.range這個屬性 /* 和key綁定的函數(shù)數(shù)組,每個函數(shù)操作一個dom節(jié)點, 一個key對應(yīng)多個dom節(jié)點,所以actionList是個function數(shù)組; */ actionList:[function(){},function(){}] }*/ } Observer.prototype = { //遍歷model中所有的屬性,每個屬性用defineKey來監(jiān)控所有屬性 monit: function(data, baseUrl) { var me = this; baseUrl = baseUrl || ""; var isTypeMatch = (data && typeof data === "object"); if (isTypeMatch) { Object.keys(data).forEach(function(key) { var base = baseUrl ? (baseUrl + "." + key) : key; me.defineKey(data, key, data[key], baseUrl); //定義自己 me.monit(data[key], base); //遞歸【定義的是下一層】 }); } }, //用到了Object.defineProperty來定義屬性,這樣屬性改變的時候,就會自動執(zhí)行里面的set方法 defineKey: function(data, key, val, baseUrl) { var me = this; var base = baseUrl ? (baseUrl + "." + key) : key; Object.defineProperty(data, key, { enumerable: true, configurable: false, get: function() { return val; }, //更新并監(jiān)控新的值,執(zhí)行publish函數(shù) set: function(newVal) { if (newVal !== val) { val = newVal; //設(shè)置新值需要重新監(jiān)控 me.monit(newVal, base); //(baseUrl+"."+key)作為觀察者模式中的監(jiān)聽的那個key,也可以說是監(jiān)聽的那個事件 me.publish(base, newVal); } } }); }, /* 根據(jù)key來執(zhí)行綁定在這個key上的所有函數(shù),比如說person.age.range這個key, 它變動的時候,publish會執(zhí)行綁定在person.age.range這個key上所有的function */ publish: function(key, newVal) { (this.subs || []).forEach(function(sub) { if (sub.key == key) { (sub.actionList || []).forEach(function(action) { action(newVal); }); } }); }, //給model中的某個key(例如person.age.range)添加綁定的function subscribe: function(key, callback) { var tgIdx; var hasExist = this.subs.some(function(unit, idx) { tgIdx = (unit.key === key) ? idx : -1; return (unit.key === key) }); if (hasExist) { if (Object.prototype.toString.call(this.subs[tgIdx].actionList)=="[object Array]"){ this.subs[tgIdx].actionList.push(callback); } else { this.subs[tgIdx].actionList = [callback]; } } else { this.subs.push({ key: key, actionList: [callback] }); } }, //取消訂閱 remove: function(key) { var removeIdx; this.subs.forEach(function(sub, idx) { removeIdx = sub.key === key ? idx : -1; return sub.key === key }); if (removeIdx !== -1) { this.subs.splice(removeIdx, 1); } }, isObject: function(data) { return data && typeof data === "object" } }; 2.Compile: 模板編譯器 var Compile = function(opts) { this.opts = opts; this.data = this.opts.data; this.observer = this.opts.observer; this.regExp = /{{([sS]*)}}/; this.ele = document.createElement("div"); this.ele.innerHTML = opts.template; //渲染頁面 this.fragment = this.transToFrament(this.ele); this.travelAllNodes(this.fragment); this.ele.appendChild(this.fragment); }; Compile.prototype = { //把頁面上的dom節(jié)點轉(zhuǎn)化成文檔碎片,防止dom頻繁操作影響頁面性能 transToFrament: function(el) { var fragment = document.createDocumentFragment(), child; // 將原生節(jié)點拷貝到fragment while (child = el.firstChild) { fragment.appendChild(child); } return fragment; }, //遍歷文檔碎片節(jié)點下所有的node節(jié)點(用到了函數(shù)遞歸調(diào)用),執(zhí)行compileNode travelAllNodes: function(ele) { this.compileNode(ele); ([].slice.call(ele.childNodes) || []).forEach(function(node) { this.compileNode(node); if (node.childNodes && node.childNodes.length) { this.travelAllNodes(node); } }.bind(this)); }, /*包含功能 1.渲染node節(jié)點 2.給key設(shè)置callback函數(shù),函數(shù)內(nèi)操作node節(jié)點 */ compileNode: function(node) { if (this.isElement(node)) { this.compileElementNode(node); } else if (this.isText(node)) { this.compileTextNode(node); } }, /* 編譯element類型的node節(jié)點, 需要處理屬性綁定v-bind="{{data.name}}"和 事件v-event="{{data.event}}" */ compileElementNode: function(node) { var me = this, nodeAttrs = node.attributes; [].slice.call(nodeAttrs).forEach(function(attr) { var attrName = attr.name; var attrValue = attr.value; var key = me.getKey(attrValue); me.bindKeyToNode(key, attr); attr.value = me.compileString(attrValue); //渲染node }); }, //編譯文本類型的node節(jié)點,里面放了對應(yīng)的"{{data.name}}"這種數(shù)據(jù)格式 compileTextNode: function(ele) { var key = this.getKey(ele.textContent); this.bindKeyToNode(key, ele); ele.textContent = this.compileString(ele.textContent); }, //解析“{{}}”,把它變成對應(yīng)的數(shù)據(jù)值 compileString: function(str) { var key = this.getKey(str); return str.replace(this.regExp, this.getValueByKey(key)); }, //綁定key和node節(jié)點,key一旦改變,就會觸發(fā)對應(yīng)的函數(shù),修改node節(jié)點 bindKeyToNode: function(key, node) { if (!!key.trim()) { console.log(key); var nodeType = node.nodeType; var regExp = new RegExp("{{" + key + "}}"); var originTextConetnt; if (nodeType === 2) { originTextConetnt = node.value; } else if (nodeType === 3) { originTextConetnt = node.textContent; } this.observer.subscribe(key, function(newVal) { var tgValue = originTextConetnt.replace(regExp, newVal); if (nodeType === 2) { node.value = tgValue; } else if (nodeType === 3) { node.textContent = tgValue; } }); } }, //從{{name.age.sex}}中獲取name.age.sex getKey: function(str) { return str.match(this.regExp) ? str.match(this.regExp)[1] : ""; }, //獲取key對應(yīng)的value值 getValueByKey: function(key) { var arr = key ? key.split(".") : []; var temp = this.data; for (var i = 0; i < arr.length; i++) { if (temp) { temp = temp[arr[i]]; } else { temp = undefined; break } } return temp; }, isElement: function(ele) { return ele.nodeType === 1 ? true : false; }, isText: function(ele) { return ele.nodeType === 3 ? true : false; }, getElement: function() { return this.ele; } } 3.ViewModel:結(jié)合Observer與Compile,實現(xiàn)model > view的數(shù)據(jù)單向綁定 var ViewModel = function(opts) { this.opts = opts; this.data = opts.data; this.wrapper = opts.wrapper; this.template = opts.template; this.Observer = (typeof Observer != undefined) ? Observer : opts.Observer; this.Compile = (typeof Compile != undefined) ? Compile : opts.Compile; this.init(); } ViewModel.prototype = { init: function() { var opts = this.opts; this.observer = new this.Observer(opts); this.observer.monit(this.data); //監(jiān)控數(shù)據(jù)變化,數(shù)據(jù)已經(jīng)改變了 this.compiler = new this.Compile(Object.assign(opts, { observer: this.observer })); //編譯生成節(jié)點 if (this.wrapper) { this.wrapper.appendChild(this.compiler.getElement()); } }, get: function() { return this.compiler.getElement(); } };
總結(jié)
簡單地調(diào)用new ViewModel({data:data,template:template}),完成了model和view的綁定,
ViewModel內(nèi)部大致執(zhí)行順序是:

1. 創(chuàng)建數(shù)據(jù)監(jiān)控對象this.observer,該對象監(jiān)控data(監(jiān)控以后,data的屬性改變,
   就會執(zhí)行defineProperty中的set函數(shù),set函數(shù)里面添加了publish發(fā)布函數(shù))

2. 創(chuàng)建模板編譯器對象this.compiler,該對象編譯template,生成最終的dom樹,
   并且給每個需要綁定數(shù)據(jù)的dom節(jié)點添加了subscribe訂閱函數(shù)

3. 最后,改變data里面的屬性,會自動觸發(fā)defineProperty中的set函數(shù),set函數(shù)調(diào)用publish函數(shù),
   publish會根據(jù)key的名稱,找到對應(yīng)的需要執(zhí)行的函數(shù)列表,依次執(zhí)行所有函數(shù)
Git地址
https://github.com/devil1989/databind/
demo
    
    
    
        
        Document
        
        
    
    
        
        
    
    


使用場景說明:
當我們想要修改頁面某個元素的信息,但又不想費勁地查找dom元素再去修改元素的值,
這種情況下,可以用demo中的數(shù)據(jù)綁定,只需修改數(shù)據(jù)的值,就實現(xiàn)了頁面元素重新渲染
請看下面的gif動畫中展示的,只要修改data.age和data.name,頁面元素就自動重新渲染了

結(jié)束語

本demo只是簡單實現(xiàn)數(shù)據(jù)綁定,很多功能并未實現(xiàn),只是提供一種思路,拋磚引玉;
如果對上述代碼中的Observer類的代碼不是很理解,可以先了解下觀察者模式以及實現(xiàn)原理;
最后,感謝大家的閱讀!!

推薦: 翻譯項目Master的自述: 1. 干貨|人人都是翻譯項目的Master 2. iKcamp出品微信小程序教學(xué)共5章16小節(jié)匯總(含視頻) 3. 開始免費連載啦~每周2更共11堂iKcamp課|基于Koa2搭建Node.js實戰(zhàn)項目教學(xué)(含視頻)| 課程大綱介紹

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/92544.html

相關(guān)文章

  • MVVM 中的動態(tài)數(shù)據(jù)綁定

    摘要:要實現(xiàn)最小化刷新,我們要將模板中的每個綁定都收集起來。思考題在最后的實現(xiàn)下,我們把模板改為下面這樣雖然很少會有人這樣寫,就會出現(xiàn)重復(fù)的實例,該如何解決這個問題,參考早期源碼學(xué)習系列之四如何實現(xiàn)動態(tài)數(shù)據(jù)綁定 上一篇文章我們了解了怎樣實現(xiàn)一個簡單模板引擎。但這個模板引擎只適合靜態(tài)模板,因為它是將模板整體編譯成字符串進行全量替換。如果每次數(shù)據(jù)改變都進行一次替換,會有兩個最主要的問題: 性能...

    Meils 評論0 收藏0
  • 用原生 JS 實現(xiàn) MVVM 框架2——單向綁定

    摘要:上一篇寫了實現(xiàn)框架的一些基本概念本篇用代碼來實現(xiàn)一個完整的框架思考假設(shè)有如下代碼,里面的會和試圖中的一一映射,修改的值,會直接引起試圖中對應(yīng)數(shù)據(jù)的變化如何實現(xiàn)上述呢回想下這篇講的觀察者模式和數(shù)據(jù)監(jiān)聽主題是什么觀察者是什么觀察者何時訂閱主題主 上一篇寫了實現(xiàn) MVVM 框架的一些基本概念 本篇用代碼來實現(xiàn)一個完整的 MVVM 框架 思考 假設(shè)有如下代碼,data里面的name會和試圖中的...

    Zoom 評論0 收藏0
  • Vue基本原理

    摘要:標簽添加監(jiān)聽事件文本節(jié)點這一步我們操作頁面輸入框,可以看到以下效果,證明監(jiān)聽事件添加有效。 前言 經(jīng)過幾天的研究,發(fā)現(xiàn)學(xué)習框架的底層技術(shù),收獲頗豐,相比只學(xué)習框架的使用要來的合算;如果工作急需,快速上手應(yīng)用,掌握如何使用短期內(nèi)更加高效;如果有較多的時間來系統(tǒng)學(xué)習,建議研究一下框架的等層技術(shù)、原理。 Vue、React、Angular三大框架對比 1、Vue Vue是尤雨溪編寫的一個構(gòu)建...

    firim 評論0 收藏0
  • Vue基本原理

    摘要:標簽添加監(jiān)聽事件文本節(jié)點這一步我們操作頁面輸入框,可以看到以下效果,證明監(jiān)聽事件添加有效。 前言 經(jīng)過幾天的研究,發(fā)現(xiàn)學(xué)習框架的底層技術(shù),收獲頗豐,相比只學(xué)習框架的使用要來的合算;如果工作急需,快速上手應(yīng)用,掌握如何使用短期內(nèi)更加高效;如果有較多的時間來系統(tǒng)學(xué)習,建議研究一下框架的等層技術(shù)、原理。 Vue、React、Angular三大框架對比 1、Vue Vue是尤雨溪編寫的一個構(gòu)建...

    ytwman 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<