Vue源碼探究-數(shù)據(jù)綁定邏輯架構(gòu)

Vue源碼探究-數(shù)據(jù)綁定邏輯架構(gòu)

數(shù)據(jù)觀察系統(tǒng)是Vue實現(xiàn)數(shù)據(jù)綁定挪拟、異步更新的核心模塊挨务,數(shù)據(jù)觀察系統(tǒng)的實現(xiàn)也是Vue源碼里最為復(fù)雜的部分,在仔細(xì)研究具體實現(xiàn)之前舞丛,先對整個數(shù)據(jù)綁定的邏輯架構(gòu)進(jìn)行一個充分的認(rèn)識耘子,會更有助于解讀源碼。

先說明一下球切,因為三個類的名稱比較容易讓人誤解,所以在以后把Observer稱作觀察目標(biāo)绒障,Watcher稱作監(jiān)視器吨凑,Dep稱作依賴對象。

數(shù)據(jù)綁定邏輯架構(gòu)

Vue的數(shù)據(jù)觀察系統(tǒng)是基于發(fā)布者/訂閱者模式户辱,數(shù)據(jù)更新觸發(fā)刷新頁面的過程主要依賴數(shù)據(jù)觀察系統(tǒng)里鐵三角關(guān)系鸵钝。在這個系統(tǒng)中,主要角色分別是 Observer庐镐、Dep恩商、Watcher 這三個對象,對于每一個角色在觀察數(shù)據(jù)更新的流程中各自承擔(dān)的職責(zé)需要深入進(jìn)行理解必逆。下面請出三個主角登場怠堪,來介紹一下它們:

Observer

Observer 相當(dāng)于觀察目標(biāo)類,在數(shù)據(jù)綁定邏輯架構(gòu)中的職責(zé)是收集需要觀察的數(shù)據(jù)對象名眉,進(jìn)行變量存取器的包裝粟矿,并遞歸地對每一個需要觀察的對象注冊發(fā)布者對象,再由發(fā)布者去注冊相應(yīng)的監(jiān)視器损拢。這里非常巧妙的是觸發(fā)通知監(jiān)視器數(shù)據(jù)更新的事件的注冊陌粹,一般的發(fā)布訂閱模式需要建立一個事件管理器或者調(diào)度中心來統(tǒng)一管理各種事件的注冊,然而Vue的數(shù)據(jù)綁定不需要這樣的機(jī)制福压,它借用 Object.defineProperty 方法來為每一個被監(jiān)視的數(shù)據(jù)設(shè)置了存取器掏秩,依靠數(shù)據(jù)的存取行為自然地實現(xiàn)了事件的觸發(fā)。在初始化Vue實例中設(shè)置的 data 屬性時荆姆,對這些輸入的數(shù)據(jù)對象對行了依賴追蹤蒙幻,包裝后的變量存放在 _data 屬性中,這個過程中發(fā)布者和監(jiān)視器的依賴添加是不可見的胞枕;而通過配置 watch 屬性顯式設(shè)置的監(jiān)視器杆煞,就可以在實例的 _watchers 私有屬性中查看到。每個組件初始化后有一個唯一的 _watcher 對象,它是一個用來監(jiān)視在 data 中注冊的數(shù)據(jù)變動從而更新視圖的監(jiān)視器决乎,它也默認(rèn)被添加到了各屬性的依賴監(jiān)視數(shù)組中队询。在每個修改為可觀察狀態(tài)的屬性中,都含有一個 Dep 實例即發(fā)布者构诚,這個對象的 subs 屬性就是用來存放依賴的所有監(jiān)視器 Watcher 實例對象蚌斩,subs 可以理解為訂閱者,即所有訂閱了該數(shù)據(jù)對象變動的監(jiān)視器的數(shù)組集合范嘱。之所以需要在一開始為數(shù)據(jù)收集依賴送膳,參考另一些開發(fā)者的總結(jié)是由于并非所有的數(shù)據(jù)都值得監(jiān)視,要知道監(jiān)視沒有用到的數(shù)據(jù)就是對性能的浪費丑蛤,在實例觀察中也確實發(fā)現(xiàn)叠聋,頁面中沒有用到的屬性,沒有被初始化為依賴項受裹,這樣即便改變了它的數(shù)值碌补,頁面也不會觸發(fā)多余的刷新。

Dep

Dep 在Vue的數(shù)據(jù)觀察者系統(tǒng)里充當(dāng)發(fā)布者的角色棉饶,它不僅用來觸發(fā)數(shù)據(jù)更新和建立依賴的事件厦章,還用來存放每一個可監(jiān)視數(shù)據(jù)所依賴的監(jiān)視器,這個正是在第一步收集依賴時的重要一環(huán)照藻。實例初始化的過程中收集了所有需要跟蹤變化的數(shù)據(jù)袜啃,在運用 Observer 重新包裝每一個屬性的同時,創(chuàng)建了各自的 dep 對象幸缕,并在get和set方法中分別使用了 Dep的兩個方法:depend 建立依賴群发,notify 通知變動。另外 Dep 還負(fù)責(zé)維護(hù)依賴監(jiān)視器的增減冀值。在構(gòu)造 Dep 類的過程中也物,定義了全局的 Dep.target 對象和 targetStack 數(shù)組,targetStack 數(shù)組是用來存放待執(zhí)行的 watcher 棧列疗,Dep.target 是用來指代當(dāng)前的監(jiān)視器滑蚯,必須唯一,它的存在對于建立監(jiān)視器的依賴起到重要作用抵栈,在重置數(shù)據(jù)的 getter 時告材,當(dāng)它存在時才執(zhí)行建立數(shù)據(jù)與監(jiān)視器的依賴,即只有顯式配置了 watch 或創(chuàng)建了 computed 變量時才會在實例的私有屬性里看到監(jiān)視器古劲。

Watcher

Watcher 是這個架構(gòu)中的監(jiān)視器斥赋,充當(dāng)觀察者的角色。在Vue實例初始化的過程中产艾,一定會默認(rèn)創(chuàng)建一個監(jiān)視器疤剑,這個監(jiān)視器就是用來監(jiān)視實例對象的數(shù)據(jù)變化用來更新視圖的滑绒,實例的私有屬性 _watcher 用來存放它。在創(chuàng)建可觀察的數(shù)據(jù)時隘膘,每一個數(shù)據(jù)的 Dep 對象會收集監(jiān)視器并建立依賴疑故,當(dāng)數(shù)據(jù)變化時,Dep 對象通知所有的監(jiān)視器執(zhí)行更新弯菊,執(zhí)行更新有兩種模式纵势,如果依賴是通過配置 computed 變量創(chuàng)建的,則會立即觸發(fā)相關(guān)的更新操作管钳,如果數(shù)據(jù)的 dep.subs 數(shù)組中沒有依賴的監(jiān)視器钦铁,則默認(rèn)惰性更新模式。Watcher 類最主要的作用是通知視圖更新才漆,眾所周知視圖的更新是非撑2埽花費時間,會影響程序性能醇滥,為了盡量減少視圖更新導(dǎo)致的性能損失躏仇,在通知視圖執(zhí)行更新操作之前會有一個緩沖時段,在這個時段中會收集最后一次監(jiān)視器收到的變更腺办,減少不必要的重復(fù)更新,實現(xiàn)最優(yōu)性能糟描。

架構(gòu)圖示

充分了解了數(shù)據(jù)觀察系統(tǒng)的三個主角之后怀喉,再來看看官網(wǎng)貼出的示意圖,就會發(fā)現(xiàn)終于能摸清Vue的數(shù)據(jù)觀察系統(tǒng)的架構(gòu)了船响,只不過渲染視圖的具體實現(xiàn)與數(shù)據(jù)觀察系統(tǒng)的交互暫時還沒有去摸索躬拢,以后會仔細(xì)地去探索,現(xiàn)在終于比較清晰地弄懂了Vue的數(shù)據(jù)綁定的原理了见间。

Vue數(shù)據(jù)觀察系統(tǒng)架構(gòu)

一個簡單的實例

為了更清晰初步了解數(shù)據(jù)綁定相關(guān)的初始化過程聊闯,創(chuàng)建了一個非常簡單的實例,data配置了兩個屬性米诉,其中 name 變量并不在頁面中使用菱蔬,還顯式設(shè)置了一個依賴 msg 的監(jiān)視器。

new Vue({
  data () {
    return {
      msg: 'hello',
      name: ''
    }
  },
  watch: {
    'msg' (value) {
      console.log('msg更新了')
    }
  }
})

下面截圖是實例的相關(guān)監(jiān)視器私有屬性史侣,_watcher 是跟蹤頁面渲染的監(jiān)視器拴泌,每個實例唯一;_wacthers 是實例所擁有的所有監(jiān)視器的集合惊橱。顯式設(shè)置的 watcher 在是數(shù)組中的第一個對象蚪腐。這里雖然看不到 Observer 背后的包裝過程,但改變了 msg 屬性之后税朴,可以看到監(jiān)視器執(zhí)行的回調(diào)顯示回季。

相關(guān)的私有屬性
視圖更新監(jiān)視器
顯式設(shè)置的watcher

從Vue對象實例化著手到開始分析數(shù)據(jù)綁定的核心實現(xiàn)家制,這一路過來還沒有真正遇到值得困擾的問題。但未曾想到的是泡一,數(shù)據(jù)綁定這個Vue的核心特色功能竟然讓我苦苦研讀了好幾天颤殴,似乎以前對于設(shè)計模式的了解顯得那樣無力。期間去搜索了一些前人做的分析說明文章以求從各個角度深入理解瘾杭,但大多數(shù)解讀讀完后依然覺得沒能很透徹地理解這個模塊诅病,后來讀到了一個簡易實現(xiàn)Vue觀察者系統(tǒng)的文章,讓我忽然對核心邏輯是如何實現(xiàn)的有了比較清晰的認(rèn)識粥烁,而且對于設(shè)計模式也有了更深入的理解贤笆。也許第一次讀源碼的時候太多非核心的技術(shù)實現(xiàn)干擾了對于核心部分的理解,也因為之前的一些知識不牢固讨阻,所以從這一次學(xué)習(xí)中得到了一個很好的經(jīng)驗芥永,要更加關(guān)注本質(zhì)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钝吮,一起剝皮案震驚了整個濱河市埋涧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌奇瘦,老刑警劉巖棘催,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異耳标,居然都是意外死亡醇坝,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門次坡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呼猪,“玉大人,你說我怎么就攤上這事砸琅∷尉啵” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵症脂,是天一觀的道長谚赎。 經(jīng)常有香客問我,道長摊腋,這世上最難降的妖魔是什么沸版? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮兴蒸,結(jié)果婚禮上视粮,老公的妹妹穿的比我還像新娘。我一直安慰自己橙凳,他們只是感情好蕾殴,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布笑撞。 她就那樣靜靜地躺著,像睡著了一般钓觉。 火紅的嫁衣襯著肌膚如雪茴肥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天荡灾,我揣著相機(jī)與錄音瓤狐,去河邊找鬼。 笑死批幌,一個胖子當(dāng)著我的面吹牛础锐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播荧缘,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼皆警,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了截粗?” 一聲冷哼從身側(cè)響起信姓,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绸罗,沒想到半個月后意推,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡珊蟀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年左痢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片系洛。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖略步,靈堂內(nèi)的尸體忽然破棺而出描扯,到底是詐尸還是另有隱情,我是刑警寧澤趟薄,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布绽诚,位于F島的核電站,受9級特大地震影響杭煎,放射性物質(zhì)發(fā)生泄漏恩够。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一羡铲、第九天 我趴在偏房一處隱蔽的房頂上張望蜂桶。 院中可真熱鬧,春花似錦也切、人聲如沸扑媚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疆股。三九已至费坊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旬痹,已是汗流浹背附井。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留两残,地道東北人永毅。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像磕昼,于是被迫代替她去往敵國和親卷雕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348

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