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

■ 1.請(qǐng)簡(jiǎn)述 Vue 首次渲染的過(guò)程。

  • 首先進(jìn)行Vue的初始化,初始化Vue的實(shí)例成員以及靜態(tài)成員。

  • 當(dāng)初始化結(jié)束之后,開(kāi)始調(diào)用構(gòu)造函數(shù),在構(gòu)造函數(shù)中調(diào)用this._init(),這個(gè)方法相當(dāng)于我們整個(gè)Vue的入口。

  • 在_init()中調(diào)用this.$mount(),共有兩個(gè)this.$mount()。

    ①第一個(gè)this.$mount()是entry-runtime-with-compiler.js入口文件,這個(gè)$mount()的核心作用是幫我們把模板編譯成render函數(shù),但它首先會(huì)判斷一下當(dāng)前是否傳入了render選項(xiàng),如果沒(méi)有傳入的話,它會(huì)去獲取我們的template選項(xiàng),如果template選項(xiàng)也沒(méi)有的話,他會(huì)把el中的內(nèi)容作為我們的模板,然后把模板編譯成render函數(shù),它是通過(guò)compileToFunctions()函數(shù),幫我們把模板編譯成render函數(shù)的,當(dāng)把render函數(shù)編譯好之后,它會(huì)把render函數(shù)存在我們的options.render中。
    => src\platforms\web\entry-runtime-with-compiler.js
    => 如果沒(méi)有傳遞render,把模版編譯成render函數(shù)
    => compileToFunction()生成render()渲染函數(shù)
    => options.render=render

    ②第二個(gè)this.$mount()是runtime/index.js中的this.$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。
    => src\platforms\web\runtime\index.js
    => mountComponent()

  • 接下來(lái)調(diào)用mountComponent(),mountComponent()是在src/core/instance/lifecycle.js中定義的,在mountComponent()中,首先會(huì)判斷render選項(xiàng),如果沒(méi)有render,但是傳入了模板,并且當(dāng)前是開(kāi)發(fā)環(huán)境的話會(huì)發(fā)送警告,警告運(yùn)行時(shí)版本不支持編譯器。接下來(lái)會(huì)觸發(fā)beforeMount這個(gè)生命周期中的鉤子函數(shù),也就是開(kāi)始掛載之前。

  • 然后定義了updateComponent(),在這個(gè)方法中,定義了_render和_update,_render的作用是生成虛擬DOM,_update的作用是將虛擬DOM轉(zhuǎn)換成真實(shí)DOM,并且掛載到頁(yè)面上來(lái)。

  • 再接下來(lái)就是創(chuàng)建Watcher對(duì)象,在創(chuàng)建Watcher時(shí),傳遞了updateComponent這個(gè)函數(shù),這個(gè)函數(shù)最終是在Watcher內(nèi)部調(diào)用的。在Watcher創(chuàng)建完之后還調(diào)用了get方法,在get方法中,會(huì)調(diào)用updateComponent()。

  • 然后觸發(fā)了生命周期的鉤子函數(shù)mounted,掛載結(jié)束,最終返回Vue實(shí)例。


    vue首次渲染

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

Vue2.0+版本 的響應(yīng)式原理核心是通過(guò) ES5 的保護(hù)對(duì)象的 Object.defindeProperty 中的訪問(wèn)器屬性中的 get 和 set 方法,data 中聲明的屬性都被添加了訪問(wèn)器屬性,當(dāng)讀取 data 中的數(shù)據(jù)時(shí)自動(dòng)調(diào)用 get 方法,當(dāng)修改 data 中的數(shù)據(jù)時(shí),自動(dòng)調(diào)用 set 方法,檢測(cè)到數(shù)據(jù)的變化,會(huì)觸發(fā)dep的notify通知觀察者 Wacher,觀察者 Wacher調(diào)用update方法自動(dòng)觸發(fā)重新render 當(dāng)前組件(子組件不會(huì)重新渲染),生成新的虛擬 DOM 樹(shù),Vue 框架會(huì)遍歷并對(duì)比新舊虛擬 DOM 樹(shù)中每個(gè)節(jié)點(diǎn)的差別,并記錄下來(lái),最后把不同點(diǎn)渲染到真實(shí) DOM 樹(shù)上。因?yàn)橛玫腛bject.defindeProperty只能對(duì)屬性進(jìn)行監(jiān)聽(tīng), 所以要深度遍歷每個(gè)對(duì)象,對(duì)象的新增刪除也檢測(cè)不到,所以vue
3.0采用了proxy,可以對(duì)整個(gè)對(duì)象進(jìn)行劫持


響應(yīng)式原理

■ 3、請(qǐng)簡(jiǎn)述虛擬 DOM 中 Key 的作用和好處。

v-for遍歷的時(shí)候,能夠追蹤每個(gè)節(jié)點(diǎn)的身份,在進(jìn)行新舊虛擬DOM節(jié)點(diǎn)比較的時(shí)候,會(huì)基于key的變化重新排列元素的順序,從而重用和重新排序現(xiàn)有的元素,并且移除key不存在的元素,方便在diff過(guò)程中找到對(duì)應(yīng)的節(jié)點(diǎn),然后復(fù)用,從而減少dom的操作。

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

 /** 通用的 Vue 文件格式*/
<template>
<!-- 模版-->
</template>

<script>
// 生命周期函數(shù) 一些事件邏輯處理
</script>

<style lang="scss" scoped rel="stylesheet/scss">
<!-- css 樣式或者 scss / less 等樣式預(yù)處理語(yǔ)言-->
</style>

編譯時(shí)主要是對(duì) template 里面的內(nèi)容進(jìn)行編譯。script 可以直接使用,style 也是可以直接抽離出來(lái)使用或者進(jìn)行樣式預(yù)處理。

整體編譯過(guò)程:

模板編譯的入庫(kù)是compileToFunctions,先從緩存中加載編譯好的render函數(shù),如果沒(méi)有就去調(diào)用compile函數(shù)合并選項(xiàng),然后調(diào)用baseCompile(參數(shù):合并好的選項(xiàng))編譯模板。之后通過(guò)調(diào)用createFunction函數(shù),把baseCompile中生成的字符串形式JS代碼轉(zhuǎn)化為函數(shù)形式。當(dāng)render和staticRenderFns初始化完畢,掛載到Vue實(shí)例的options對(duì)應(yīng)的屬性上

baseCompile函數(shù)

第一步將 模板字符串 轉(zhuǎn)換成 element ASTs(parser 解析器)

<div>
  <p>{{name}}</p>
</div>

把上面轉(zhuǎn)為ASTs

{
  tag: "div"
  type: 1,
  // staticRoot: false,
  // static: false,
  plain: true,
  parent: undefined,
  attrsList: [],
  attrsMap: {},
  children: [
      {
      tag: "p"
      type: 1,
      staticRoot: false,
      // static: false,
      plain: true,
      parent: {tag: "div", ...},
      attrsList: [],
      attrsMap: {},
      children: [{
          type: 2,
          text: "{{name}}",
          // static: false,
          expression: "_s(name)"
      }]
    }
  ]
}

第二步是對(duì) AST 進(jìn)行靜態(tài)根節(jié)點(diǎn),靜態(tài)節(jié)點(diǎn)標(biāo)記,主要用來(lái)做虛擬 DOM 的渲染優(yōu)化(optimizer 優(yōu)化器)
主要是循環(huán)把上面的代碼的 staticRoot 及 static 設(shè)為 true / false,patch過(guò)程中會(huì)跳過(guò)靜態(tài)根節(jié)點(diǎn)
(只包含純文本的靜態(tài)節(jié)點(diǎn)不是靜態(tài)根節(jié)點(diǎn),因?yàn)榇藭r(shí)的優(yōu)化成本大于收益)

{
  tag: "div"
  type: 1,
  staticRoot: false,
  static: false,
  plain: true,
  parent: undefined,
  attrsList: [],
  attrsMap: {},
  children: [
      {
      tag: "p"
      type: 1,
      staticRoot: false,
      static: false,
      plain: true,
      parent: {tag: "div", ...},
      attrsList: [],
      attrsMap: {},
      children: [{
          type: 2,
          text: "{{name}}",
          static: false,
          expression: "_s(name)"
      }]
    }
  ]
}

第三步是 使用 element ASTs 生成 render 函數(shù)代碼字符串(generate代碼生成器)
主要是把上面的 element ASTs 轉(zhuǎn)成下面的 render 函數(shù)代碼串, _c 是 createElement,執(zhí)行后也是創(chuàng)建 vnode 節(jié)點(diǎn)

with(this){
  return _c(
    'div',
    [
      _c(
        'p',
        [
          _v(_s(name))
        ]
      )
    ]
  )
}
模板編譯
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,310評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,951評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,796評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,566評(píng)論 6 407
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,055評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,303評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,799評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,683評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,899評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,135評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,520評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,757評(píng)論 1 282
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,528評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,844評(píng)論 2 372