vue源碼解讀 -- 雙向綁定

雙向綁定是MVVM框架中一個(gè)基本也是很誘人的功能,它能夠讓我們減少dom操作,進(jìn)而極大減少我們的代碼量。我們可以通過指令把一個(gè)input的value值和Model中字段關(guān)聯(lián),而不再需要dom取出此節(jié)點(diǎn),然后獲取節(jié)點(diǎn)的value。
本文以v-model指令為例,分析vue中雙向綁定的基本實(shí)現(xiàn)。在vue源碼解讀 -- 整體架構(gòu)一文中,我們已經(jīng)分析vue的基本運(yùn)行機(jī)制,本文就不再對(duì)這些方面的內(nèi)容進(jìn)行展開。

一個(gè)例子

如下面的例子,當(dāng)input框中的值改變時(shí),p標(biāo)簽中的值也跟著變化

<div id="app">
  <input type="text" v-model="a" />
  <p>{{a}}</p>
</div>

<script>
  var model = new Vue({
    el: '#app',
    data: {
      a: 1
    }
  });
</script>```

#####基本原理
DOM結(jié)構(gòu)進(jìn)行compile后生成linker函數(shù),linker函數(shù)執(zhí)行后返回一個(gè)unlinker函數(shù)供后續(xù)節(jié)點(diǎn)移除時(shí)調(diào)用(接觸事件綁定、指令監(jiān)聽等)。linker函數(shù)執(zhí)行會(huì)生成DOM中調(diào)用到的指令列表,指令經(jīng)過優(yōu)先級(jí)排序,然后執(zhí)行bind操作:

dirs[i]._bind()

一個(gè)Directive對(duì)象的構(gòu)成如下所示。el指DOM節(jié)點(diǎn);expression表示指令中的表達(dá)式;name是基礎(chǔ)指令的名稱,通過它找到對(duì)應(yīng)的方法;priority表示優(yōu)先級(jí)。

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/1975863-c8b91641484af0b4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

Directive的_bind函數(shù)主要做了兩件事情:調(diào)用基本指令的bind和生成watcher對(duì)象。 前者是指令自身邏輯的確定;后者是綁定data的view的橋梁,當(dāng)數(shù)據(jù)變化時(shí)會(huì)通過update實(shí)現(xiàn)指令更新。

#####指令的bind和update

######1. v-text

{{}}和v-text都會(huì)調(diào)用text指令,下面是其bind和update方法:bind方法進(jìn)行一些預(yù)處理工作,update則自己進(jìn)行view的更新。

bind () {
// 節(jié)點(diǎn)為element時(shí) 通過textContent更新
// 節(jié)點(diǎn)為text時(shí),通過data更新
this.attr = this.el.nodeType === 3
? 'data'
: 'textContent'
},
update (value) {
this.el[this.attr] = _toString(value)
}


######2. v-model
例子中的v-model,其指令的bind和update方法如下:

bind () {
...
if (tag === 'INPUT') {
handler = handlers[el.type] || handlers.text
...
handler.bind.call(this)
this.update = handler.update
this._unbind = handler.unbind
},

handler的bind和update方法:

bind () {
var self = this
// Now attach the main listener
this.listener = function () {
var val = number || isRange ? toNumber(el.value) : el.value
self.set(val)
nextTick(function () {
if (self._bound && !self.focused) {
self.update(self._watcher.value)
}
})
}
...
this.on('change', this.listener)
if (!lazy) {
this.on('input', this.listener)
}
...
},
update (value) {
this.el.value = _toString(value)
},

v-model指令中的bind對(duì)input的change和input事件進(jìn)行了監(jiān)聽,當(dāng)input中值改變是,會(huì)調(diào)用Directive的set方法,然后一步調(diào)用update方法。set方法的邏輯如下:

Directive.prototype.set = function (value) {
if (this.twoWay) {
this._withLock(function () {
this._watcher.set(value)
})
}
...
}

Watcher.prototype.set = function (value) {
var scope = this.scope || this.vm
// 如果有過濾器 執(zhí)行過濾器邏輯
if (this.filters) {
value = scope._applyFilters(
value, this.value, this.filters, true)
}
try {
// 調(diào)用setter更新vm中的值 同事執(zhí)行依賴的notify
this.setter.call(scope, scope, value)
}
...
}


上述就是vue實(shí)現(xiàn)雙向綁定的一個(gè)基本思路,其根本還是通過DOM的事件體系進(jìn)行監(jiān)聽。當(dāng)數(shù)據(jù)變化時(shí),首先改變vm中的值,然后調(diào)用依賴的notify方法實(shí)現(xiàn)其他View的同步更新。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容,還有我對(duì)于 Vue 1.0 印象不深的內(nèi)容。關(guān)于...
    云之外閱讀 5,168評(píng)論 0 29
  • Vue 實(shí)例 屬性和方法 每個(gè) Vue 實(shí)例都會(huì)代理其 data 對(duì)象里所有的屬性:var data = { a:...
    云之外閱讀 2,361評(píng)論 0 6
  • 1.安裝 可以簡(jiǎn)單地在頁面引入Vue.js作為獨(dú)立版本,Vue即被注冊(cè)為全局變量,可以在頁面使用了。 如果希望搭建...
    Awey閱讀 11,278評(píng)論 4 129
  • 下載安裝搭建環(huán)境 可以選npm安裝,或者簡(jiǎn)單下載一個(gè)開發(fā)版的vue.js文件 瀏覽器打開加載有vue的文檔時(shí),控制...
    冥冥2017閱讀 6,193評(píng)論 0 42
  • 有人說“要將生命的每一天當(dāng)做最后一天來度過?!边@樣的人生才會(huì)有意義。只有在這時(shí),才會(huì)明白什么事情是最重要的,做過什...
    沐清小寨閱讀 479評(píng)論 0 1

友情鏈接更多精彩內(nèi)容