01.Vue.js 運行機制全局概覽

Vue.js 運行機制全局概覽

全局概覽

這一節(jié)筆者將為大家介紹一下 Vue.js 內(nèi)部的整個流程,希望能讓大家對全局有一個整體的印象,然后我們再來逐個模塊進行講解。從來沒有了解過 Vue.js 實現(xiàn)的同學(xué)可能會對一些內(nèi)容感到疑惑,這是很正常的,這一節(jié)的目的主要是為了讓大家對整個流程有一個大概的認識,算是一個概覽預(yù)備的過程,當把整本小冊認真讀完以后,再來閱讀這一節(jié),相信會有收獲的。
首先我們來看一下筆者畫的內(nèi)部流程圖。


大家第一次看到這個圖一定是一頭霧水的,沒有關(guān)系,我們來逐個講一下這些模塊的作用以及調(diào)用關(guān)系。相信講完之后大家對Vue.js內(nèi)部運行機制會有一個大概的認識。

初始化及掛載


new Vue() 之后。 Vue 會調(diào)用 _init 函數(shù)進行初始化,也就是這里的 init 過程,它會初始化生命周期、事件、 props、 methods、 data、 computed 與 watch 等。其中最重要的是通過 Object.defineProperty 設(shè)置 settergetter 函數(shù),用來實現(xiàn)「響應(yīng)式」以及「依賴收集」,后面會詳細講到,這里只要有一個印象即可。
初始化之后調(diào)用 $mount會掛載組件,如果是運行時編譯,即不存在 render function 但是存在 template 的情況,需要進行「編譯」步驟。

編譯

compile編譯可以分成 parseoptimizegenerate 三個階段,最終需要得到 render function。

  • parse
    parse 會用正則等方式解析 template 模板中的指令、class、style等數(shù)據(jù),形成AST。
  • optimize
    optimize 的主要作用是標記 static 靜態(tài)節(jié)點,這是 Vue 在編譯過程中的一處優(yōu)化,后面當 update 更新界面時,會有一個 patch 的過程, diff 算法會直接跳過靜態(tài)節(jié)點,從而減少了比較的過程,優(yōu)化了 patch 的性能。
  • generate
    generate 是將 AST 轉(zhuǎn)化成 render function 字符串的過程,得到結(jié)果是 render 的字符串以及 staticRenderFns 字符串。
    在經(jīng)歷過 parseoptimizegenerate 這三個階段以后,組件中就會存在渲染 VNode 所需的 render function 了。
響應(yīng)式

接下來也就是 Vue.js 響應(yīng)式核心部分。


這里的 gettersetter 已經(jīng)在之前介紹過了,在 init 的時候通過 Object.defineProperty 進行了綁定,它使得當被設(shè)置的對象被讀取的時候會執(zhí)行 getter 函數(shù),而在當被賦值的時候會執(zhí)行 setter 函數(shù)。
當 render function 被渲染的時候,因為會讀取所需對象的值,所以會觸發(fā) getter 函數(shù)進行「依賴收集」,「依賴收集」的目的是將觀察者 Watcher 對象存放到當前閉包中的訂閱者 Dep 的 subs 中。形成如下所示的這樣一個關(guān)系。

在修改對象的值的時候,會觸發(fā)對應(yīng)的 setter, setter 通知之前「依賴收集」得到的 Dep 中的每一個 Watcher,告訴它們自己的值改變了,需要重新渲染視圖。這時候這些 Watcher 就會開始調(diào)用 update 來更新視圖,當然這中間還有一個 patch 的過程以及使用隊列來異步更新的策略,這個我們后面再講。

Virtual DOM

我們知道,render function 會被轉(zhuǎn)化成 VNode 節(jié)點。Virtual DOM 其實就是一棵以 JavaScript 對象( VNode 節(jié)點)作為基礎(chǔ)的樹,用對象屬性來描述節(jié)點,實際上它只是一層對真實 DOM 的抽象。最終可以通過一系列操作使這棵樹映射到真實環(huán)境上。由于 Virtual DOM 是以 JavaScript 對象為基礎(chǔ)而不依賴真實平臺環(huán)境,所以使它具有了跨平臺的能力,比如說瀏覽器平臺、Weex、Node 等。

比如說下面這樣一個例子:

{
  tag: 'div',           /*說明這是一個div標簽*/
  children: [           /*存放該標簽的子節(jié)點*/
    {
      tag: 'a',         /*說明這是一個a標簽*/
      text: 'click me'  /*標簽的內(nèi)容*/
    }
  ]
}

渲染后可以得到

<div>
  <a>click me</a>
</div>

這只是一個簡單的例子,實際上的節(jié)點有更多的屬性來標志節(jié)點,比如 isStatic (代表是否為靜態(tài)節(jié)點)、 isComment (代表是否為注釋節(jié)點)等。

更新視圖


前面我們說到,在修改一個對象值的時候,會通過 setter -> Watcher -> update 的流程來修改對應(yīng)的視圖,那么最終是如何更新視圖的呢?

當數(shù)據(jù)變化后,執(zhí)行 render function 就可以得到一個新的 VNode 節(jié)點,我們?nèi)绻胍玫叫碌囊晥D,最簡單粗暴的方法就是直接解析這個新的 VNode 節(jié)點,然后用 innerHTML 直接全部渲染到真實 DOM 中。但是其實我們只對其中的一小塊內(nèi)容進行了修改,這樣做似乎有些「浪費」。

那么我們?yōu)槭裁床荒苤恍薷哪切父淖兞说牡胤健鼓??這個時候就要介紹我們的「patch」了。我們會將新的 VNode 與舊的 VNode 一起傳入 patch 進行比較,經(jīng)過 diff 算法得出它們的「差異」。最后我們只需要將這些「差異」的對應(yīng) DOM 進行修改即可。

再看全局

回過頭再來看看這張圖,是不是大腦中已經(jīng)有一個大概的脈絡(luò)了呢?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容