Vue.js 運行機制全局概覽
全局概覽
這一節(jié)筆者將為大家介紹一下 Vue.js 內(nèi)部的整個流程陪拘,希望能讓大家對全局有一個整體的印象钟些,然后我們再來逐個模塊進行講解搭儒。從來沒有了解過 Vue.js 實現(xiàn)的同學(xué)可能會對一些內(nèi)容感到疑惑扇丛,這是很正常的,這一節(jié)的目的主要是為了讓大家對整個流程有一個大概的認識擒滑,算是一個概覽預(yù)備的過程,當(dāng)把整本小冊認真讀完以后叉弦,再來閱讀這一節(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è)置 setter
與 getter
函數(shù)川无,用來實現(xiàn)「響應(yīng)式」以及「依賴收集」,后面會詳細講到虑乖,這里只要有一個印象即可懦趋。
初始化之后調(diào)用 $mount
會掛載組件,如果是運行時編譯疹味,即不存在 render function 但是存在 template 的情況仅叫,需要進行「編譯」步驟。
編譯
compile編譯可以分成 parse
糙捺、optimize
與 generate
三個階段诫咱,最終需要得到 render function。
parse
parse
會用正則等方式解析 template 模板中的指令洪灯、class坎缭、style等數(shù)據(jù),形成AST签钩。
optimize
optimize
的主要作用是標記 static 靜態(tài)節(jié)點掏呼,這是 Vue 在編譯過程中的一處優(yōu)化,后面當(dāng) update
更新界面時边臼,會有一個 patch
的過程哄尔, diff 算法會直接跳過靜態(tài)節(jié)點,從而減少了比較的過程柠并,優(yōu)化了 patch
的性能岭接。
generate
generate
是將 AST 轉(zhuǎn)化成 render function 字符串的過程,得到結(jié)果是 render 的字符串以及 staticRenderFns 字符串臼予。
在經(jīng)歷過 parse
鸣戴、optimize
與 generate
這三個階段以后,組件中就會存在渲染 VNode 所需的 render function 了粘拾。
響應(yīng)式
接下來也就是 Vue.js 響應(yīng)式核心部分窄锅。
這里的 getter
跟 setter
已經(jīng)在之前介紹過了,在 init
的時候通過 Object.defineProperty
進行了綁定缰雇,它使得當(dāng)被設(shè)置的對象被讀取的時候會執(zhí)行 getter
函數(shù)入偷,而在當(dāng)被賦值的時候會執(zhí)行 setter
函數(shù)追驴。
當(dāng) render function 被渲染的時候,因為會讀取所需對象的值疏之,所以會觸發(fā) getter
函數(shù)進行「依賴收集」殿雪,「依賴收集」的目的是將觀察者 Watcher 對象存放到當(dāng)前閉包中的訂閱者 Dep 的 subs 中。形成如下所示的這樣一個關(guān)系锋爪。
在修改對象的值的時候丙曙,會觸發(fā)對應(yīng)的 setter
, setter
通知之前「依賴收集」得到的 Dep 中的每一個 Watcher其骄,告訴它們自己的值改變了亏镰,需要重新渲染視圖。這時候這些 Watcher 就會開始調(diào)用 update
來更新視圖拯爽,當(dāng)然這中間還有一個 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)的視圖租冠,那么最終是如何更新視圖的呢鹏倘?
當(dā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ò)了呢?
那么晤揣,讓我們繼續(xù)學(xué)習(xí)每一個模塊吧!