Vuejs源碼解析總結(jié)--響應(yīng)式原理

作為剛?cè)胄蠽ue的小白磕谅,看了不少Vue響應(yīng)式原理的文章私爷,還是不太明白,要么說的太復(fù)雜膊夹,要么說的不夠詳細衬浑。因此,我專門研究了一下具體的代碼放刨,并參考了滴滴團隊的《Vue.js權(quán)威指南》一書中的深入響應(yīng)式原理一章工秩,總結(jié)了能通俗易懂的相關(guān)知識。

由于手機上編輯不便进统,暫時沒有加入代碼事例助币,等以后補充。

正文:


圖片發(fā)自簡書App

先上傳一張官方的圖解麻昼。

Vuejs實現(xiàn)響應(yīng)式主要靠三步:

1奠支、對數(shù)據(jù)進行處理。使得數(shù)據(jù)能夠?qū)?shù)據(jù)變化有反應(yīng)抚芦。

2倍谜、對v-text這些指令做處理迈螟。賦予他們改變DOM中對應(yīng)數(shù)據(jù)的方法,類似update(value)就把頁面上的插值改成value

3尔崔、提供watcher答毫。通過watcher把數(shù)據(jù)的命令傳達給指令

響應(yīng)式原理主要包括三個部分:Data、Watcher季春、Directive(即v-text這些指令)

先說Data部分:

Vuejs有個生命周期洗搂,第一步就是vm._initData方法。這個方法主要干了兩件事:

1载弄、_proxy代理數(shù)據(jù)耘拇。

就是把data中的數(shù)據(jù)代理到vm實例上,那么以后我們訪問vm.times就等于訪問vm._data.times宇攻,代理的過程就是利用Object.defineProperty()方法惫叛,具體實現(xiàn)可以看源碼。

2逞刷、observe數(shù)據(jù)嘉涌。

這步的工作主要是上面提到的賦予數(shù)據(jù)有感知變化的能力,俗話說觀測數(shù)據(jù)夸浅。也就是通過Object.defineProperty方法定義數(shù)據(jù)新的setter()和getter()方法仑最,可見,Object.defineProperty是核心方法帆喇。

這里的observe(val)針對的是具體的val警医,也就是說如果val是數(shù)組,就遍歷番枚,直至單個元素法严。

具體observe干了什么呢?

① 對每個數(shù)據(jù)new了一個Dep實例葫笼,即dep深啤。

這個dep的作用主要是用來訂閱Watcher實例的,因為你一個數(shù)據(jù)可能多出要用路星,要與多個Watcher實例鏈接起來溯街。說是訂閱,其實就是把這些Watcher實例存到這個dep的一個subs數(shù)組屬性中洋丐。

② 重新定義了getter方法呈昔。

這個方法主要是把上面創(chuàng)建的那個dep傳給當前正在計算的watcher實例(這叫依賴收集)。什么叫當前正在計算的watcher實例友绝?可以這樣理解堤尾,有一個全局變量,在依次初始化每個watcher的時候迁客,會把自己存到這個全局變量中去以供獲取郭宝。

③ 重新定義了setter方法

只有一步辞槐,就是dep.notify(),這個方法主要是調(diào)用上面那個當時數(shù)據(jù)的dep來遍歷它subs數(shù)組里面的watcher實例粘室,并調(diào)用他們的update方法來更新榄檬。俗話說就是,通知跟這個數(shù)據(jù)有關(guān)的watcher實例要更新了衔统。

至此鹿榜,data部分結(jié)束。

directive部分

指令太多锦爵,就以一個v-text為例:hello {{world}}舱殿。

瀏覽器得認識插值,于是進行編譯险掀,調(diào)用_compile方法怀薛。這個方法主要有3步。

① 將template編譯成document fragment迷郑,然后深度遍歷編譯Dom樹,即編譯每個節(jié)點创倔。

② 每個節(jié)點如何編譯的呢嗡害?

以文本節(jié)點"hello {{world}}"為例,將其解析成一個tokens數(shù)組畦攘,有兩個對象霸妹,一個是僅有value="hello "的對象,另一個是包含html知押、oneTime叹螟、tag、value等字段的對象(就是插值那部分)台盯。

然后新建一個document fragment罢绽,遍歷這個tokens,分別創(chuàng)建dom插到這個document fragment里静盅,如果是有tag字段的對象(即插值那個對象)良价,還會擴展其token對象,即在有之前的字段的情況下蒿叠,再增加一個descriptor字段明垢,值是一個對象,包括def市咽、expression痊银、name等字段,最后返回一個linkFn的函數(shù)施绎。

注意溯革,這里返回的是一個函數(shù)贞绳,解析完每個節(jié)點后返回的是一個函數(shù)。函數(shù)的作用是如果滿足前面token對象里的一些字段鬓照,就調(diào)用vm._bindDir()方法創(chuàng)建指令對象熔酷,指令對象的屬性也是根據(jù)token對象里的字段設(shè)置,并且豺裆,這個指令對象實例還要放到vm實例的directives數(shù)組中去拒秘,以便于后面比如遍歷使用。

注意臭猜,此處函數(shù)并沒有調(diào)用躺酒,只是作為返回值提供。

③ 所有節(jié)點編譯的最后會遍歷所有節(jié)點的linkFn方法蔑歌,在此處才執(zhí)行羹应,這個時候所有的指令對象就創(chuàng)建了,于是下一步就將這些指令對象排序并綁定一些比如update方法次屠,這里的update方法是指令自身的园匹,會更改dom。

在對每個指令對象綁定的過程中都會new一個watcher實例劫灶,并把指令對象的update方法傳給watcher實例作為一個回調(diào)裸违,此處指令對象就與watcher建立起了鏈接。watcher實例是在此處new的本昏。

Watcher部分

watcher主要用來鏈接data和directive供汛。

① 將插值表達式轉(zhuǎn)成帶有exp和get()方法的對象。并把get()方法賦給這個watcher實例的getter涌穆。

這個get()方法的作用就是傳一個vm進去怔昨,會返回vm.exp的值

② 調(diào)用Watcher實例自身的get()方法。

第一步設(shè)置自己為當前正在計算的Watcher宿稀,即上面Data部分提到的那個全局變量趁舀。

第二步利用call方法取一次該數(shù)據(jù)。

如何獲取的原叮?即this.getter.call(vm,vm)赫编,getter方法在第一步定義了,會對vm中數(shù)據(jù)執(zhí)行一次取值操作奋隶。

比如上面插值中的world擂送,這個時候會觸發(fā)world數(shù)據(jù)的getter方法,即一方面收集到這個world數(shù)據(jù)的dep(依賴收集)唯欣,另一方面會讓world數(shù)據(jù)訂閱到我這個watcher實例(訂閱)嘹吨,加入到world數(shù)據(jù)dep的屬性subs數(shù)組里。

③ 數(shù)據(jù)改變時境氢,world數(shù)據(jù)的setter方法會觸發(fā)蟀拷,即dep.notify()方法觸發(fā)碰纬,于是遍歷了dep的subs數(shù)組,并調(diào)用了里面每個watcher實例的update方法问芬。

watcher實例的update方法只有在一部分情況下會直接調(diào)用悦析,大部分情況下會延遲執(zhí)行。

如何延遲執(zhí)行的呢此衅?

即把當前watcher實例推入一個內(nèi)部隊列中强戴,這個隊列會通過nextTick方法在下一個事件循環(huán)周期處理,遍歷執(zhí)行挡鞍,這樣可以只觸發(fā)一次DOM操作骑歹。

watcher實例是如何update的呢?

我們得比較新舊值看是否需要更新墨微。

舊值就是之前調(diào)用this.get()獲得的值道媚。

新值就再次執(zhí)行上面的步驟②!步驟②會重新再取一次值翘县,并重新收集依賴及訂閱最域,如果值一樣就沒操作,如果不一樣锈麸,就調(diào)用watcher實例創(chuàng)建時羡宙,directive傳入的那個回調(diào),那個回調(diào)就是指令對象自己的update方法掐隐,這樣就可以更新具體的值了。

至此結(jié)束钞馁!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末虑省,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子僧凰,更是在濱河造成了極大的恐慌探颈,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件训措,死亡現(xiàn)場離奇詭異伪节,居然都是意外死亡,警方通過查閱死者的電腦和手機绩鸣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門怀大,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人呀闻,你說我怎么就攤上這事化借。” “怎么了捡多?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵蓖康,是天一觀的道長铐炫。 經(jīng)常有香客問我,道長蒜焊,這世上最難降的妖魔是什么倒信? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮泳梆,結(jié)果婚禮上鳖悠,老公的妹妹穿的比我還像新娘。我一直安慰自己鸭丛,他們只是感情好竞穷,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鳞溉,像睡著了一般瘾带。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上熟菲,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天看政,我揣著相機與錄音,去河邊找鬼抄罕。 笑死允蚣,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的呆贿。 我是一名探鬼主播嚷兔,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼做入!你這毒婦竟也來了冒晰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤竟块,失蹤者是張志新(化名)和其女友劉穎壶运,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浪秘,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡蒋情,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了耸携。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棵癣。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖夺衍,靈堂內(nèi)的尸體忽然破棺而出浙巫,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布的畴,位于F島的核電站渊抄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏丧裁。R本人自食惡果不足惜护桦,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望煎娇。 院中可真熱鬧二庵,春花似錦、人聲如沸缓呛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哟绊。三九已至因妙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間票髓,已是汗流浹背攀涵。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留洽沟,地道東北人以故。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像裆操,于是被迫代替她去往敵國和親怒详。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

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

  • 這方面的文章很多踪区,但是我感覺很多寫的比較抽象棘利,本文會通過舉例更詳細的解釋。(此文面向的Vue新手們朽缴,如果你是個大牛...
    Ivy_2016閱讀 15,373評論 8 64
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)水援,斷路器密强,智...
    卡卡羅2017閱讀 134,601評論 18 139
  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容,還有我對于 Vue 1.0 印象不深的內(nèi)容蜗元。關(guān)于...
    云之外閱讀 5,045評論 0 29
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法或渤,類相關(guān)的語法,內(nèi)部類的語法奕扣,繼承相關(guān)的語法薪鹦,異常的語法,線程的語...
    子非魚_t_閱讀 31,587評論 18 399
  • 我的github: vue雙向綁定原理 MVC模式 以往的MVC模式是單向綁定,即Model綁定到View池磁,當我們...
    KlausXu閱讀 44,818評論 7 91