Vue.js 源碼剖析-響應(yīng)式原理、虛擬 DOM封拧、模板編譯和組件化

2020.11.01

1志鹃、請(qǐng)簡(jiǎn)述 Vue 首次渲染的過程。
image.png

首次渲染總結(jié):

  • 在首次渲染之前,首先進(jìn)行Vue初始化,初始化實(shí)例成員和靜態(tài)成員
  • 當(dāng)初始化結(jié)束之后,要調(diào)用Vue的構(gòu)造函數(shù)new Vue(),在構(gòu)造函數(shù)中調(diào)用了_init()方法,這個(gè)方法相當(dāng)于我們整個(gè)Vue的入口
  • 在_init方法中,最終調(diào)用了$mount,一共有兩個(gè)$mount,第一個(gè)定義在entry-runtime-with-compiler.js文件中,也就是我們的入口文件$mount,這個(gè)$mount()的核心作用是幫我們把模板編譯成render函數(shù)泽西,但它首先會(huì)判斷一下當(dāng)前是否傳入了render選項(xiàng)曹铃,如果沒有傳入的話,它會(huì)去獲取我們的template選項(xiàng)捧杉,如果template選項(xiàng)也沒有的話陕见,他會(huì)把el中的內(nèi)容作為我們的模板,然后把模板編譯成render函數(shù)糠溜,它是通過compileToFunctions()函數(shù)淳玩,幫我們把模板編譯成render函數(shù)的,當(dāng)把render函數(shù)編譯好之后,它會(huì)把render函數(shù)存在我們的options.render中非竿。
  • 接著會(huì)調(diào)用src/platforms/web/runtime/index.js文件中的$mount方法,在這個(gè)中首先會(huì)重新獲取el蜕着,因?yàn)槿绻沁\(yùn)行時(shí)版本的話,是不會(huì)走entry-runtime-with-compiler.js這個(gè)入口中獲取el,所以如果是運(yùn)行時(shí)版本的話承匣,我們會(huì)在runtime/index.js的$mount()中重新獲取el蓖乘。
  • 接下來調(diào)用mountComponent(),這個(gè)方法在src/core/instance/lifecycle.js中定義的,在mountComponent()中韧骗,首先會(huì)判斷render選項(xiàng)嘉抒,如果沒有render選項(xiàng),但是我們傳入了模板袍暴,并且當(dāng)前是開發(fā)環(huán)境的話會(huì)發(fā)送一個(gè)警告些侍,目的是如果我們當(dāng)前使用運(yùn)行時(shí)版本的Vue,而且我們沒有傳入render,但是傳入了模版,告訴我們運(yùn)行時(shí)版本不支持編譯器。接下來會(huì)觸發(fā)beforeMount這個(gè)生命周期中的鉤子函數(shù)政模,也就是開始掛載之前岗宣。
  • 然后定義了updateComponent(),在這個(gè)函數(shù)中淋样,調(diào)用vm._render和vm._update耗式,vm._render的作用是生成虛擬DOM,vm._update的作用是將虛擬DOM轉(zhuǎn)換成真實(shí)DOM趁猴,并且掛載到頁面上
  • 創(chuàng)建Watcher對(duì)象刊咳,在創(chuàng)建Watcher時(shí),傳遞了updateComponent這個(gè)函數(shù)儡司,這個(gè)函數(shù)最終是在Watcher內(nèi)部調(diào)用的娱挨。在Watcher內(nèi)部會(huì)用了get方法,當(dāng)Watcher創(chuàng)建完成之后,會(huì)觸發(fā)生命周期中的mounted鉤子函數(shù)捕犬。在watcher 中的get方法(創(chuàng)建完watcher會(huì)調(diào)用一次)中让蕾,會(huì)調(diào)用updateComponent(),
  • 掛載結(jié)束或听,最終返回Vue實(shí)例。

2笋婿、請(qǐng)簡(jiǎn)述 Vue 響應(yīng)式原理誉裆。


image.png
  • Vue的響應(yīng)式是從Vue的實(shí)例init()方法中開始的,在init()方法中先調(diào)用initState()初始化Vue實(shí)例的狀態(tài)缸濒,在initState方法中調(diào)用了initData()足丢, initData()是把data屬性注入到Vue實(shí)例上,并且調(diào)用observe(data)將data對(duì)象轉(zhuǎn)化成響應(yīng)式的對(duì)象庇配。

  • observe是響應(yīng)式的入口, 在observe(value)中斩跌,首先判斷傳入的參數(shù)value是否是對(duì)象,如果不是對(duì)象直接返回捞慌。再判斷value對(duì)象是否有_ob_這個(gè)屬性耀鸦,如果有說明做過了響應(yīng)式處理,則直接返回;如果沒有袖订,創(chuàng)建observer對(duì)象氮帐,并且返回observer`對(duì)象。

  • 在創(chuàng)建observer對(duì)象時(shí)洛姑,給當(dāng)前的value對(duì)象定義不可枚舉的_ob_屬性上沐,記錄當(dāng)前的observer對(duì)象,然后再進(jìn)行數(shù)組的響應(yīng)式處理和對(duì)象的響應(yīng)式處理楞艾。數(shù)組的響應(yīng)式處理就是設(shè)置數(shù)組的幾個(gè)特殊的方法参咙,push、pop硫眯、shift等蕴侧,然后找到數(shù)組對(duì)象中的_ob_對(duì)象中的dep,調(diào)用dep的notify()方法,再遍歷數(shù)組中每一個(gè)成員舟铜,對(duì)每個(gè)成員調(diào)用observer()戈盈,如果這個(gè)成員是對(duì)象的話,也會(huì)轉(zhuǎn)換成響應(yīng)式對(duì)象谆刨。對(duì)象的響應(yīng)式處理塘娶,就是調(diào)用walk方法,walk方法就是遍歷對(duì)象的每一個(gè)屬性痊夭,對(duì)每個(gè)屬性調(diào)用defineReactive方法

  • defineReactive會(huì)為每一個(gè)屬性創(chuàng)建對(duì)應(yīng)的dep對(duì)象刁岸,讓dep去收集依賴,如果當(dāng)前屬性的值是對(duì)象她我,會(huì)調(diào)用observe虹曙。defineReactive中最核心的方法是getter 和 setter。getter 的作用是收集依賴番舆,收集依賴時(shí), 為每一個(gè)屬性收集依賴酝碳,如果這個(gè)屬性的值是對(duì)象,那也要為子對(duì)象收集依賴恨狈,最后返回屬性的值疏哗。在setter 中,先保存新值禾怠,如果新值是對(duì)象返奉,也要調(diào)用 observe ,把新設(shè)置的對(duì)象也轉(zhuǎn)換成響應(yīng)式的對(duì)象,然后派發(fā)更新(發(fā)送通知)吗氏,調(diào)用dep.notify()

  • 收集依賴時(shí)芽偏,在watcher對(duì)象的get方法中調(diào)用pushTarget, 記錄Dep.target屬性;訪問data中的成員的時(shí)候收集依賴弦讽,defineReactive的getter中收集依賴污尉,把屬性對(duì)應(yīng)的 watcher 對(duì)象添加到dep的subs數(shù)組中,給childOb收集依賴,目的是子對(duì)象添加和刪除成員時(shí)發(fā)送通知十厢。

  • 在數(shù)據(jù)發(fā)生變化的時(shí)候等太,會(huì)調(diào)用dep.notify()發(fā)送通知,dep.notify()會(huì)調(diào)用watcher對(duì)象的update()方法蛮放,update()中的調(diào)用的queueWatcher()會(huì)去判斷watcher是否被處理缩抡,如果這個(gè)watcher對(duì)象沒有的話添加到queue隊(duì)列中,并調(diào)用flushScheduleQueue()包颁,在flushScheduleQueue()中觸發(fā)beforeUpdate鉤子函數(shù)調(diào)用watcher.run():run()-->get() --> getter() --> updateComponent()瞻想; 然后清空上一次的依賴;觸發(fā)actived的鉤子函數(shù)娩嚼;觸發(fā)updated鉤子函數(shù)

3蘑险、請(qǐng)簡(jiǎn)述虛擬 DOM 中 Key 的作用和好處。
key值的作用岳悟,其實(shí)是:追蹤列表中哪些元素被添加佃迄、被修改、被移除的輔助標(biāo)志贵少。就是他可以幫助我們快速對(duì)比兩個(gè)虛擬dom對(duì)象呵俏,找到虛擬dom對(duì)象被修改的元素,然后僅僅替換掉被修改的元素滔灶,然后再生成新的真實(shí)dom

好處: 可以減少 dom 的操作普碎,減少 diff 和渲染所需要的時(shí)間,提升了性能录平。

4麻车、請(qǐng)簡(jiǎn)述 Vue 中模板編譯的過程。


image.png
  • 模版編譯入口函數(shù)compileToFunctions,內(nèi)部首先從緩存加載編譯好的render函數(shù)斗这,如果緩存中沒有动猬,則調(diào)用compile,開始編譯
  • 在compile 函數(shù)中表箭,首先合并選項(xiàng)options枣察,然后再調(diào)用baseCompile 編譯模版。 compile的核心是處理選項(xiàng)options燃逻, 真正處理是在basCompile中完成的
  • 模版和合并好的選項(xiàng)傳遞給baseCompile, 這里面完成了模版編譯的核心三件事情。 parse()- 首先把模版字符串轉(zhuǎn)化為AST 對(duì)象臂痕,也就是抽象語法樹伯襟;optimize() - 然后對(duì)抽象語法樹進(jìn)行優(yōu)化,標(biāo)記ASTtree 中的靜態(tài)sub tree, 檢測(cè)到靜態(tài)子樹握童,設(shè)置為靜態(tài)姆怪,不需要在每次重新渲染的時(shí)候重新生成節(jié)點(diǎn),patch的過程中會(huì)過靜態(tài)子樹;generator() - 最后把優(yōu)化過的AST對(duì)象稽揭,轉(zhuǎn)化為字符串形式的代碼俺附。 執(zhí)行完成之后,會(huì)回到入口函數(shù)complieToFunctions
  • compileToFunction, 會(huì)繼續(xù)把字符串代碼轉(zhuǎn)化為函數(shù)溪掀,通過調(diào)用createFunction事镣,當(dāng)render 和 staticRenderFns初始化完畢,最終會(huì)掛在到Vue實(shí)例的options對(duì)應(yīng)的屬性中
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末揪胃,一起剝皮案震驚了整個(gè)濱河市璃哟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌喊递,老刑警劉巖随闪,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異骚勘,居然都是意外死亡宙刘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門报账,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纵势,“玉大人,你說我怎么就攤上這事藐石〖垂” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵于微,是天一觀的道長(zhǎng)逗嫡。 經(jīng)常有香客問我,道長(zhǎng)株依,這世上最難降的妖魔是什么驱证? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮恋腕,結(jié)果婚禮上抹锄,老公的妹妹穿的比我還像新娘。我一直安慰自己荠藤,他們只是感情好伙单,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哈肖,像睡著了一般吻育。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上淤井,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天布疼,我揣著相機(jī)與錄音摊趾,去河邊找鬼。 笑死游两,一個(gè)胖子當(dāng)著我的面吹牛砾层,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贱案,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肛炮,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了轰坊?” 一聲冷哼從身側(cè)響起铸董,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肴沫,沒想到半個(gè)月后粟害,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颤芬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年悲幅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片站蝠。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡汰具,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出菱魔,到底是詐尸還是另有隱情留荔,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布澜倦,位于F島的核電站聚蝶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏藻治。R本人自食惡果不足惜碘勉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望桩卵。 院中可真熱鬧验靡,春花似錦、人聲如沸雏节。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钩乍。三九已至兼蕊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間件蚕,已是汗流浹背孙技。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留排作,地道東北人牵啦。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像妄痪,于是被迫代替她去往敵國(guó)和親哈雏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353