摘要:下圖展示了實現(xiàn)雙向綁定的流程實現(xiàn)一個簡單的雙向綁定雙向綁定最最最初級進(jìn)階版操作是非常耗時和好性能,所以在優(yōu)化過程中先從操作入手。
接觸Vue有一段時間了,但是對于其雙向綁定的實現(xiàn)一直是似懂非懂,今天看到一篇寫的比較好的文章 傳送門1 根據(jù)原作者的指導(dǎo)自己也去實現(xiàn)了一遍簡單的 demo (本文的demo均基于Object.defineProperty 實現(xiàn)數(shù)據(jù)劫持,利用了對Vue.js實現(xiàn)雙向綁定的思想)
[注]本文所有圖片均來自于:傳送門2
前言 幾種主流的雙向綁定1.發(fā)布-訂閱模式
2.臟值檢測
通過對比數(shù)據(jù)是否有變更,來決定是否更新視圖。最簡單的可以通過定時輪詢?nèi)z測數(shù)據(jù)的變動。當(dāng)然Google不會這么low, Angular 只有在指定事件觸發(fā)時進(jìn)入臟值檢測:
DOM事件,比如用戶輸入文本點擊按鈕等(ng-click)
XHR響應(yīng)事件
瀏覽器 Location 變更
Timer事件
執(zhí)行 $digidt() 或 $apply()
3.數(shù)據(jù)劫持
Vue.js 采用的是 數(shù)據(jù)劫持+發(fā)布/訂閱模式 的方式,通過 Object.defineProperty() 來劫持各個屬性的 setter/getter, 在數(shù)據(jù)變動時發(fā)布消息給訂閱者(Wacther), 觸發(fā)相應(yīng)的監(jiān)聽回調(diào)。下圖展示了Vue實現(xiàn)雙向綁定的流程
進(jìn)階版demo雙向綁定最最最初級demo
DOM操作是非常耗時和好性能,所以在優(yōu)化過程中先從DOM操作入手。因為遍歷解析過程中有多次DOM操作,為了提高性能和效率,需要一種方法來避免對DOM元素的直接封裝操作。在Vue使用 DocumentFragment 作為替代容器。
DocumentFragment 接口表示一個沒有父級文件的最小文檔對象。它被當(dāng)做一個輕量版本的Document 使用。所以使用 DocumentFragment 代替DOM直接處理,可以提高性能和速度。
//將傳入 node 的子節(jié)點進(jìn)行劫持,經(jīng)過處理后重新掛載回目標(biāo)節(jié)點 function convertNode(node,vm){ var fragment = document.createDocumentFragment(), child while(child = node.firstChild){ //將原生節(jié)點拷貝到 fragment,并刪除之前的child節(jié)點 fragment.appendChild(child) } return fragment } var dom = convertNode(document.getElementById("app")) document.getElmentById("app").appendChild(dom)實現(xiàn)Complie解析模板指令
Complie 主要做的事情就是解析模板指令,將模板中的變量替換為數(shù)據(jù)。所以要遍歷整個DOM樹,進(jìn)行掃描解析編譯,調(diào)用對應(yīng)的指令渲染函數(shù)進(jìn)行渲染,并調(diào)用對應(yīng)的指令更新函數(shù)進(jìn)行綁定
{{text}}
function convertNode(node,vm){ //... while(child = node.firstChild){ Compile(child,vm) fragment.appendChild(child) } return fragment } function Compile(node,vm){ var reg = /{{(.*)}}/ if(node.nodeType===1){ var attr = node.attributes //對所有屬性進(jìn)行解析 for(var i=0;iViewModel 層向 View 層的數(shù)據(jù)綁定 接下來實現(xiàn)一個 Xin 構(gòu)造器,通過 Compile 來解析模板指令,通過 Observer 監(jiān)聽屬性數(shù)據(jù)的變化實現(xiàn) Model 層向 View 層的數(shù)據(jù)綁定
function Xin(options){ this.data = options.data Observer(this.data,this) var id = options.el var dom = convertNode(document.getElementById(id),this) document.getElementById(id).appendChild(dom) }新建一個 vm 實例來測試一下 Model --> View 的綁定情況
var vm = new Xin({ el:"app", data:{ text:"Hello MVVM" } })View 層向 viewModel 層的數(shù)據(jù)綁定實際上,在 Observer 中我們已經(jīng)通過 數(shù)據(jù)劫持 實現(xiàn)了監(jiān)聽每個數(shù)據(jù)的變化,在控制臺打印 console.log(val) 就可以實時看到數(shù)據(jù)的變化。所以接下來實現(xiàn)的關(guān)鍵就是怎么用監(jiān)聽到的數(shù)據(jù)去更新視圖。
在這里,我們?nèi)崿F(xiàn)一個 Wacther 可以將它理解為觀察者 ,他的作用是能夠接收從 Observer 發(fā)過來的屬性變動通知, 然后根據(jù)屬性的變動更新視圖 update。在監(jiān)聽過程中,為所有的 data 屬性生成一個主題對象 Dep,Dep中包含需要維護(hù)的觀察者列表。每當(dāng)主題對象狀態(tài)發(fā)生變化時,其相關(guān)依賴都會得到通知,并且被自動更新(數(shù)據(jù)變動會觸發(fā)notify,再調(diào)用訂閱者的update() 方法)
function Dep(){ this.subs=[] //訂閱者隊列 } Dep.prototype={ addSub:function(sub){ this.subs.push(sub) }, notify:function(){ this.subs.forEach(function(sub){ sub.update() }) } } funcion Watcher(vm,node,bindName){ //將全局Dep.target設(shè)置為當(dāng)前頁面元素node Dep.target = this //完成watcher的初始化 this.name = bindName this.node = node this.vm = vm this.update() //初次綁定時進(jìn)行更新 Dep.target = null //保證Dep.target唯一 } Watcher.prototype = { get:function(){ this.value = this.vm.data[this.name] }, update:function(){ this.get() this.node.nodeValue = this.value } } function Observer(obj,vm){ //... Object.defineProperty(obj,prop,{ get:function(){...}, set:function(newVal){ if(val == newVal) return val = newVal //data屬性被修改,由dep觸發(fā)view層更新 dep.notify() } }) }考慮這樣一個問題,什么時候會有雙向綁定? viewModel --> view 可能會發(fā)生在所有類型的DOM節(jié)點上,而 view --> viewModel 只能發(fā)生在 input, select, textarea 等交互控件上。所以將文本節(jié)點包裝成 Watcher , 添加相關(guān)元素的觀察者列表中,Watcher 負(fù)責(zé)更新頁面元素
function Compile(node,vm){ //... if(node.nodeType ===3){ //文本節(jié)點類型 if(reg.test(node.nodeValue)){ var bindName = RegExp.$1.trim() new Watcher = (vm,node,bindName) //為該頁面元素node生產(chǎn)watcher } } }更新本文中實現(xiàn)模板渲染的方法借鑒了 Vue 1.x 中實現(xiàn)模板渲染的方法。
資料參考
Vue 2.x 模板渲染 方法借鑒React 中的 VirtualDOM,基于 VirtualDOM。 Vue 2.x 還支持服務(wù)端渲染SSR1.https://github.com/DMQ/mvvm#_2
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/91898.html
摘要:廢話不多說直接看效果圖代碼很好理解,但是在看代碼之前需要知道雙向綁定的原理其實就是基于實現(xiàn)的雙向綁定官方傳送門這里我們用官方的話來說方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現(xiàn)有屬性,并返回這個對象。 廢話不多說直接看效果圖 showImg(https://segmentfault.com/img/bVbiYY5?w=282&h=500); 代碼很好理解,但是在看代碼之前...
摘要:廢話不多說直接看效果圖代碼很好理解,但是在看代碼之前需要知道雙向綁定的原理其實就是基于實現(xiàn)的雙向綁定官方傳送門這里我們用官方的話來說方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現(xiàn)有屬性,并返回這個對象。 廢話不多說直接看效果圖 showImg(https://segmentfault.com/img/bVbiYY5?w=282&h=500); 代碼很好理解,但是在看代碼之前...
摘要:對于前端,有時候需要實現(xiàn)視圖層和數(shù)據(jù)層的雙向綁定例如當(dāng)前流行的各種框架和類庫。為代表前端數(shù)據(jù)劫持。參考資料實現(xiàn)數(shù)據(jù)雙向綁定的三種方式談?wù)勚械碾p向數(shù)據(jù)綁定非常簡單的雙向數(shù)據(jù)綁定框架三 對于前端,有時候需要實現(xiàn)視圖層和數(shù)據(jù)層的雙向綁定(two-way-binding), 例如當(dāng)前流行的各種框架和類庫:Vue.js、Angular.js、React.js。 然而,他們最原始的實現(xiàn)方式其實都相...
閱讀 3583·2021-09-22 15:50
閱讀 3262·2019-08-30 15:54
閱讀 2784·2019-08-30 14:12
閱讀 3085·2019-08-30 11:22
閱讀 2112·2019-08-29 11:16
閱讀 3606·2019-08-26 13:43
閱讀 1224·2019-08-23 18:33
閱讀 946·2019-08-23 18:32