vue源碼閱讀復(fù)盤-watcher模塊

回顧目標(biāo)

數(shù)據(jù)驅(qū)動視圖:

  1. 理解watcher玄括、dep、observer這三個對象之間的關(guān)系
  2. 這和VUE對象又有什么關(guān)系盆顾?
  3. 這和視圖又有什么關(guān)系锥惋?

敘述過程

先徹底理解了一下VUE的簡介,并寫出了一份建議書褂删。關(guān)鍵詞有漸進式飞醉、自底向上逐層應(yīng)用、聲明式開發(fā)屯阀、組件化缅帘。

之后了解了觀察者模式,觀察者模式的初衷是建立低耦合的通信機制难衰,定義對象間的一種一對多的依賴關(guān)系股毫,當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新召衔。

記得以前看過一本掘金小冊铃诬,其中作者的結(jié)論是:每個vue對象對應(yīng)一個watcher,每個observer和watcher通過dep建立一對多關(guān)系苍凛。

然后開始走讀代碼趣席。大概讀了一遍watcher、observer醇蝴、deb發(fā)現(xiàn)沒什么新的收獲宣肚,開始認(rèn)為每一個vue對象管理一個watcher,然后每個變量都管理一個observer,每個observer管理一個sub悠栓,由observer通知watcher霉涨,然后渲染按价。

但是提出了一個關(guān)鍵性的問題,有一個公共的target是用來存放watcher的笙瑟,發(fā)現(xiàn)這個target和依賴關(guān)系非常有關(guān)楼镐,然后啟動瀏覽器用檢查器監(jiān)察這個值,發(fā)現(xiàn)這個值一般是null往枷,然后打開vscode框产,全局查找,找出是什么時候?qū)懙倪@個值错洁,最后定位這個target在watch讀數(shù)據(jù)*的時候被緩存秉宿。

這段話非常的重要,只有當(dāng)執(zhí)行用戶自定義函數(shù)的時候屯碴,才會建立依賴關(guān)系描睦。

翻看了文檔。找到了一段清晰的代碼导而,

var vm = new Vue({
      data: { a: 1 },
      computed: {
        // 僅讀取
        aDouble: function () {
          return this.a * 2
        },
        // 讀取和設(shè)置
        aPlus: {
          get: function () {
            return this.a + 1
          },
          set: function (v) {
            this.a = v - 1
          }
        }
      }
    })

然后帶著上面的問題酌摇,去閱讀了vue的構(gòu)造過程,在initState中發(fā)現(xiàn)了computed的構(gòu)造,其中有一個關(guān)鍵的用法:

function initComputed (vm: Component) {
    const computed = vm.$options.computed
    ...
    const userDef = computed[key]
    makeComputedGetter(userDef, vm)
    ...
}

    function makeComputedGetter (getter: Function
    , owner: Component): Function {
        const watcher = new Watcher(owner, getter, noop, {
            lazy: true
        })
    ...
    }

對于觀察者來說用戶定義的函數(shù)是getter嗡载,也就是對于框架底層來說窑多,程序員寫的computed函數(shù),對組件來說是getter洼滚。computed實際上是用包裝了一下用戶定義的函數(shù)埂息。可以把他理解成一種特殊的watcher遥巴,根據(jù)官方的文檔千康,提到了computed有緩存功能,不會更新相同的結(jié)果铲掐。簡單起見我們就把computed當(dāng)作watcher理解拾弃。

然后開始關(guān)注這個函數(shù)內(nèi)部發(fā)生了什么:

...
computed: {
        // 僅讀取
        aDouble: function () {
          return this.a * 2
        },
...

這里讀取了this.a。我想起來vue內(nèi)部的變量都是處理過的摆霉。用了observe這個工廠方法加工過豪椿,set和get都和watcher和deb耦合。執(zhí)行aDouble這個函數(shù),computed就作為一個watcher被Dep.target記住了携栋,而a就是一個obsserver,調(diào)用了get就會將watcher保存在deps數(shù)組中,就好像a調(diào)用get就被aDouble盯上(watch)了,下次a變化(調(diào)用a.set)就重新執(zhí)行一遍aDouble搭盾。

這個時候我們再回去看wacher對象,發(fā)現(xiàn)這個對象有一個cb存放回調(diào)函數(shù)婉支,而且代碼中還出來了屬于VNode模塊的patch鸯隅。cb應(yīng)該是負(fù)責(zé)重新渲染視圖。

所以這就可以解釋VUE是如何驅(qū)動視圖改變的向挖,源自于一種自動更新的一對多機制蝌以。這可以解決父子組件的通信問題炕舵,父子組件狀態(tài)同步可以通過向子組件注入父組件的數(shù)據(jù)引用,可以實現(xiàn)單向數(shù)據(jù)流跟畅。

查看了官方文檔咽筋,結(jié)果發(fā)現(xiàn):

每個組件實例都對應(yīng)一個 watcher 實例,它會在組件渲染的過程中把“接觸”過的數(shù)據(jù)屬性記錄為依賴碍彭。之后當(dāng)依賴項的 setter 觸發(fā)時晤硕,會通知 watcher悼潭,從而使它關(guān)聯(lián)的組件重新渲染庇忌。

證實了掘金那個作者說的沒錯,每一個vue的確有一個watcher

查看watcher.get的call stack:

get (vue.js:647)
Watcher (vue.js:638)
Vue.$watch (vue.js:1254)
createWatcher (vue.js:1232)
initWatch (vue.js:1217)
initState (vue.js:1105)
Vue._init (vue.js:2110)
Vue (vue.js:2150)
(anonymous) (app.js:7)

我們看到Vue.$watch說明vue下確實有一個watcher

這個watcher目的是什么呢舰褪?我們回去看了一下watch的構(gòu)造函數(shù)

constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: Object = {}
  ) {
     ... 
     this.getter = parsePath(expOrFn)
     this.value = this.lazy
           ? undefined
           : this.get()
     ...
  }

在parsePath之中會觸發(fā)所有vue的內(nèi)部對象的get的函數(shù)句柄皆疹,然后被執(zhí)行,所有的內(nèi)部數(shù)據(jù)改動都會導(dǎo)致wacher重新渲染數(shù)據(jù)

評估結(jié)果:

結(jié)果不等于目標(biāo)

  1. 開始否認(rèn)了認(rèn)為每一個vue對象管理一個watcher
  2. 我們推導(dǎo)出了computed和watcher的關(guān)系,并認(rèn)為這是watch唯一的作用

結(jié)果等于目標(biāo)

  1. 理解watcher占拍、dep略就、observer這三個對象之間的關(guān)系
    每一個用戶定義回調(diào)函數(shù)管理一個watcher,然后每個VUE內(nèi)部data都管理一個observer,每個observer管理一個sub晃酒,由observer通知watcher表牢。
  2. 這和VUE對象又有什么關(guān)系
    VUE對象保存了data數(shù)據(jù),每一個data都管理一個observer贝次,computed是一種特殊的watcher,VUE對象渲染視圖的時候會要求watcher返回一個值崔兴,這個時候watcher執(zhí)行用戶定義的函數(shù)句柄,也就是computed當(dāng)中定義的函數(shù)時蛔翅,每個data被讀取的時候會和當(dāng)前的函數(shù)建立依賴關(guān)系敲茄,當(dāng)這個data更新的時候會重新渲染視圖,重新執(zhí)行用戶定義函數(shù)山析。且當(dāng)new一個vuew對象的時候watcher會監(jiān)聽所有的內(nèi)部數(shù)據(jù)對象堰燎。
  3. 這和視圖又有什么關(guān)系
    每個watcher負(fù)責(zé)自動更新視圖。

分析原因

開始否認(rèn)為每一個vue對象管理一個watcher

  1. 先看了別人的文章然后帶著結(jié)論去看的代碼笋轨,最后只是找怎么支持這個結(jié)論的代碼段秆剪。但是發(fā)現(xiàn)了文章之外的內(nèi)容誤以為文章錯誤了。
  2. watcher是被vue對象直接調(diào)用的爵政,很容易讓人聯(lián)想一對一關(guān)系鸟款,但不確定
  3. dep.target作為關(guān)鍵的變量,作搞清楚這個變量的作用茂卦,就可以回答誰依賴誰這個問題何什。

我們推導(dǎo)出了computed和watcher的關(guān)系,并認(rèn)為這是唯一的作用

  1. 當(dāng)看到watcher.value時等龙。意識到一個vue對象可能需要維護多個watcher处渣,因此原來的假設(shè)不成立伶贰,那么需要找到watcher具體是做什么的。沿著watcher.get一直往下看罐栈,發(fā)現(xiàn)了watcher的作用和computed有關(guān)聯(lián)黍衙,因此認(rèn)為computed是watcher的一種表現(xiàn),而一個vue對象又有n個computed荠诬。這才認(rèn)為作者的觀點是錯誤的琅翻。
  2. 很多文章中沒有找到關(guān)于computed和watcher的作用,誤以為是別人理解錯了柑贞,其實這和我的結(jié)論并不沖突方椎。

推演規(guī)律

自然語言更加容易被人理解,因為首因效應(yīng)钧嘶,人對事物的理解不太容易改變棠众,那么我們看別人的文章,我們理解的不是原作者的設(shè)計思想有决,而是經(jīng)過了非原作者的幾層解釋的最終結(jié)果闸拿。

那么這就導(dǎo)致了我對其他人的結(jié)論有一種懷疑,想找出反證的點书幕。而這個時候就需要從官方文檔中找到依據(jù)新荤,作者和團隊的文檔最具有權(quán)威和可行度。

從非自然語言——代碼台汇】凉牵總結(jié)出來的,我們可以在閱讀源代碼參考別人的文章励七,但是其中最有價值的部分智袭,不是別人的結(jié)論,而是別人閱讀的順序掠抬,看別人是如何抓住主干去理解的吼野。綜上有兩個東西對閱讀源代碼有益,分別是文章的行文順序和文章的大標(biāo)題两波,文章的大標(biāo)題一般是對源代碼模塊的高度概括瞳步。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市腰奋,隨后出現(xiàn)的幾起案子单起,更是在濱河造成了極大的恐慌,老刑警劉巖劣坊,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘀倒,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機测蘑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門灌危,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人碳胳,你說我怎么就攤上這事勇蝙。” “怎么了挨约?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長诫惭。 經(jīng)常有香客問我翁锡,道長,這世上最難降的妖魔是什么贝攒? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任盗誊,我火速辦了婚禮时甚,結(jié)果婚禮上隘弊,老公的妹妹穿的比我還像新娘。我一直安慰自己荒适,他們只是感情好梨熙,可當(dāng)我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著刀诬,像睡著了一般咽扇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陕壹,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天质欲,我揣著相機與錄音,去河邊找鬼糠馆。 笑死嘶伟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的又碌。 我是一名探鬼主播九昧,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼毕匀!你這毒婦竟也來了铸鹰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤皂岔,失蹤者是張志新(化名)和其女友劉穎蹋笼,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡剖毯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年诞仓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片速兔。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡墅拭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涣狗,到底是詐尸還是另有隱情谍婉,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布镀钓,位于F島的核電站穗熬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏丁溅。R本人自食惡果不足惜唤蔗,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窟赏。 院中可真熱鬧妓柜,春花似錦、人聲如沸涯穷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拷况。三九已至作煌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赚瘦,已是汗流浹背粟誓。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留起意,地道東北人鹰服。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像杜恰,于是被迫代替她去往敵國和親获诈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容