深入理解vue.js雙向綁定的實(shí)現(xiàn)原理
前言
大家都知道Vue.js最核心的功能有兩個(gè),一是響應(yīng)式的數(shù)據(jù)綁定系統(tǒng),二是組件系統(tǒng)。本文僅探究幾乎所有Vue的開篇介紹都會(huì)提到的hello world雙向綁定是怎樣實(shí)現(xiàn)的。先講涉及的知識(shí)點(diǎn),再參考源碼,用盡可能少的代碼實(shí)現(xiàn)那個(gè)hello world開篇示例。
一、訪問(wèn)器屬性
訪問(wèn)器屬性是對(duì)象中的一種特殊屬性,它不能直接在對(duì)象中設(shè)置,而必須通過(guò)defineProperty()
方法單獨(dú)定義。
var obj = { }; // 為obj定義一個(gè)名為hello的訪問(wèn)器屬性 Object.defineProperty(obj, "hello", { get: function () {return sth}, set: function (val) {/* do sth */} })
obj.hello
// 可以像普通屬性一樣讀取訪問(wèn)器屬性
訪問(wèn)器屬性的"值"比較特殊,讀取或設(shè)置訪問(wèn)器屬性的值,實(shí)際上是調(diào)用其內(nèi)部特性:get
和set
函數(shù)。
obj.hello
// 讀取屬性,就是調(diào)用get
函數(shù)并返回get
函數(shù)的返回值
obj.hello = "abc"
// 為屬性賦值,就是調(diào)用set
函數(shù),賦值其實(shí)是傳參
get
和set
方法內(nèi)部的this都指向obj,這意味著get
和set
函數(shù)可以操作對(duì)象內(nèi)部的值。另外,訪問(wèn)器屬性的會(huì)"覆蓋"同名的普通屬性,因?yàn)樵L問(wèn)器屬性會(huì)被優(yōu)先訪問(wèn),與其同名的普通屬性則會(huì)被忽略(也就是所謂的被"劫持"了)。
二、極簡(jiǎn)雙向綁定的實(shí)現(xiàn)
此例實(shí)現(xiàn)的效果是:隨文本框輸入文字的變化,span中會(huì)同步顯示相同的文字內(nèi)容;在js或控制臺(tái)顯式的修改obj.name
的值,視圖會(huì)相應(yīng)更新。這樣就實(shí)現(xiàn)了model =>view
以及view => model
的雙向綁定,并且是響應(yīng)式的。
以上就是Vue實(shí)現(xiàn)雙向綁定的基本原理。
三、分解任務(wù)
上述示例僅僅是為了說(shuō)明原理。
我們最終要實(shí)現(xiàn)的是:
首先將該任務(wù)分成幾個(gè)子任務(wù):
1、輸入框以及文本節(jié)點(diǎn)與data中的數(shù)據(jù)綁定
2、輸入框內(nèi)容變化時(shí),data中的數(shù)據(jù)同步變化。即view => model
的變化。
3、data中的數(shù)據(jù)變化時(shí),文本節(jié)點(diǎn)的內(nèi)容同步變化。即model => view
的變化。
要實(shí)現(xiàn)任務(wù)一,需要對(duì)DOM進(jìn)行編譯,這里有一個(gè)知識(shí)點(diǎn):DocumentFragment。
四、DocumentFragment
DocumentFragment(文檔片段)可以看作節(jié)點(diǎn)容器,它可以包含多個(gè)子節(jié)點(diǎn),當(dāng)我們將它插入到DOM中時(shí),只有它的子節(jié)點(diǎn)會(huì)插入目標(biāo)節(jié)點(diǎn),所以把它看作一組節(jié)點(diǎn)的容器。使用DocumentFragment處理節(jié)點(diǎn),速度和性能遠(yuǎn)遠(yuǎn)優(yōu)于直接操作DOM。Vue進(jìn)行編譯時(shí),就是將掛載目標(biāo)的所有子節(jié)點(diǎn)劫持(真的是劫持)到DocumentFragment中,經(jīng)過(guò)一番處理后,再將DocumentFragment整體返回插入掛載目標(biāo)。
五、數(shù)據(jù)初始化綁定
以上代碼實(shí)現(xiàn)了任務(wù)一,我們可以看到,hello world已經(jīng)呈現(xiàn)在輸入框和文本節(jié)點(diǎn)中。
六、響應(yīng)式的數(shù)據(jù)綁定
再來(lái)看任務(wù)二的實(shí)現(xiàn)思路:當(dāng)我們?cè)谳斎肟蜉斎霐?shù)據(jù)的時(shí)候,首先觸發(fā)input
事件(或者keyup
、change
事件),在相應(yīng)的事件處理程序中,我們獲取輸入框的value
并賦值給vm實(shí)例的text
屬性。我們會(huì)利用defineProperty
將data中的text
劫持為vm的訪問(wèn)器屬性,因此給vm.text
賦值,就會(huì)觸發(fā)set
方法。
在set
方法中主要做兩件事,第一是更新屬性的值,第二留到任務(wù)三再說(shuō)。
任務(wù)二也就完成了,text屬性值會(huì)與輸入框的內(nèi)容同步變化:
七、訂閱/發(fā)布模式(subscribe&publish)
text屬性變化了,set
方法觸發(fā)了,但是文本節(jié)點(diǎn)的內(nèi)容沒有變化。如何讓同樣綁定到text的文本節(jié)點(diǎn)也同步變化呢?這里又有一個(gè)知識(shí)點(diǎn):訂閱發(fā)布模式。
訂閱發(fā)布模式(又稱觀察者模式)定義了一種一對(duì)多的關(guān)系,讓多個(gè)觀察者同時(shí)監(jiān)聽某一個(gè)主題對(duì)象,這個(gè)主題對(duì)象的狀態(tài)發(fā)生改變時(shí)就會(huì)通知所有觀察者對(duì)象。
發(fā)布者發(fā)出通知 => 主題對(duì)象收到通知并推送給訂閱者 => 訂閱者執(zhí)行相應(yīng)操作
之前提到的,當(dāng)set
方法觸發(fā)后做的第二件事就是作為發(fā)布者發(fā)出通知:“我是屬性text,我變了”。文本節(jié)點(diǎn)則是作為訂閱者,在收到消息后執(zhí)行相應(yīng)的更新操作。
八、雙向綁定的實(shí)現(xiàn)
回顧一下,每當(dāng)new
一個(gè)Vue,主要做了兩件事:第一個(gè)是監(jiān)聽數(shù)據(jù):observe(data)
,第二個(gè)是編譯HTML:nodeToFragement(id)
。
在監(jiān)聽數(shù)據(jù)的過(guò)程中,會(huì)為data中的每一個(gè)屬性生成一個(gè)主題對(duì)象dep。
在編譯HTML的過(guò)程中,會(huì)為每個(gè)與數(shù)據(jù)綁定相關(guān)的節(jié)點(diǎn)生成一個(gè)訂閱者watcher,watcher會(huì)將自己添加到相應(yīng)屬性的dep中。
我們已經(jīng)實(shí)現(xiàn):修改輸入框內(nèi)容 => 在事件回調(diào)函數(shù)中修改屬性值 => 觸發(fā)屬性的set
方法。
接下來(lái)我們要實(shí)現(xiàn)的是:發(fā)出通知dep.notify()
=> 觸發(fā)訂閱者的update
方法 => 更新視圖。
這里的關(guān)鍵邏輯是:如何將watcher添加到關(guān)聯(lián)屬性的dep中。
在編譯HTML過(guò)程中,為每個(gè)與data關(guān)聯(lián)的節(jié)點(diǎn)生成一個(gè)Watcher。Watcher函數(shù)中發(fā)生了什么呢?
首先,將自己賦給了一個(gè)全局變量Dep.target
;
其次,執(zhí)行了update
方法,進(jìn)而執(zhí)行了get
方法,get
的方法讀取了vm的訪問(wèn)器屬性,從而觸發(fā)了訪問(wèn)器屬性的get
方法,get
方法中將該watcher添加到了對(duì)應(yīng)訪問(wèn)器屬性的dep中;
再次,獲取屬性的值,然后更新視圖。
最后,將Dep.target
設(shè)為空。因?yàn)樗侨肿兞?,也是watcher與dep關(guān)聯(lián)的唯一橋梁,任何時(shí)刻都必須保證Dep.target
只有一個(gè)值。
至此,hello world雙向綁定就基本實(shí)現(xiàn)了。文本內(nèi)容會(huì)隨輸入框內(nèi)容同步變化,在控制器中修改vm.text
的值,會(huì)同步反映到文本內(nèi)容中。
完整代碼:https://github.com/bison1994/two-way-data-binding
總結(jié)
以上就是關(guān)于vue.js雙向綁定實(shí)現(xiàn)原理的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- Vue2實(shí)現(xiàn)組件props雙向綁定
- vue.js數(shù)據(jù)綁定的方法(單向、雙向和一次性綁定)
- Vue2.0利用 v-model 實(shí)現(xiàn)組件props雙向綁定的優(yōu)美解決方案
- Vue 實(shí)現(xiàn)雙向綁定的四種方法
- Vuejs第一篇之入門教程詳解(單向綁定、雙向綁定、列表渲染、響應(yīng)函數(shù))
- Vue父子組件雙向綁定傳值的實(shí)現(xiàn)方法
- Vue.js每天必學(xué)之?dāng)?shù)據(jù)雙向綁定
- Vue.js實(shí)現(xiàn)雙向數(shù)據(jù)綁定方法(表單自動(dòng)賦值、表單自動(dòng)取值)
- Angular和Vue雙向數(shù)據(jù)綁定的實(shí)現(xiàn)原理(重點(diǎn)是vue的雙向綁定)
- Vue2.x和Vue3.x的雙向綁定原理詳解
相關(guān)文章
vue3實(shí)現(xiàn)監(jiān)聽store中state狀態(tài)變化的簡(jiǎn)單方法
這篇文章主要給大家介紹了關(guān)于vue3實(shí)現(xiàn)監(jiān)聽store中state狀態(tài)變化的簡(jiǎn)單方法,store是一個(gè)狀態(tài)管理工具,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10vue中PC端地址跳轉(zhuǎn)移動(dòng)端的操作方法
最近小編接到一個(gè)項(xiàng)目pc端和移動(dòng)端是兩個(gè)獨(dú)立的項(xiàng)目,兩個(gè)項(xiàng)目項(xiàng)目中的內(nèi)容基本相同,鏈接組合的方式都有規(guī)律可循,接到的需求便是在移動(dòng)端訪問(wèn)pc端的URL連接時(shí),重定向至移動(dòng)端對(duì)應(yīng)頁(yè)面,下面小編給大家分享實(shí)現(xiàn)過(guò)程,一起看看吧2021-11-11Vue el使用el-checkbox-group復(fù)選框進(jìn)行單選框方式
這篇文章主要介紹了Vue el使用el-checkbox-group復(fù)選框進(jìn)行單選框方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10VUE實(shí)現(xiàn)一個(gè)Flappy Bird游戲的示例代碼
這篇文章主要介紹了VUE實(shí)現(xiàn)一個(gè)Flappy Bird的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Vue實(shí)現(xiàn) 點(diǎn)擊顯示再點(diǎn)擊隱藏效果(點(diǎn)擊頁(yè)面空白區(qū)域也隱藏效果)
這篇文章主要介紹了Vue實(shí)現(xiàn) 點(diǎn)擊顯示 再點(diǎn)擊隱藏 點(diǎn)擊頁(yè)面空白區(qū)域也隱藏效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01vuejs實(shí)現(xiàn)折疊面板展開收縮動(dòng)畫效果
這篇文章主要介紹了vuejs實(shí)現(xiàn)折疊面板展開收縮動(dòng)畫效果,文中通過(guò)代碼給大家分享兩種情況介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09