Vue 原理解析概述 (3)

PY
6 min readFeb 15, 2019

--

組件生成與生命週期

組件化是 Vue 一大特點,Vue 把每個組件的 HTML CSS JS 放在一起,維護起來相當方便,組件化就像在拼拼圖一樣把不同組件拼成一個網站,接下來研究看看組件如何生成 Virtual Dom 然後 render 。

import Vue from 'vue'
import App from './App.vue'

var app = new Vue({
el: '#app',
// 這裡的 h 是 createElement 方法
render: h => h(App)
})

引入組件方式 render 畫面

用組件生成 Virtual Dom

  1. createComponent

上一章提到 createElement 方法 src/core/vdom/create-element.js 裡面的 tag 除了字串模板以外,還能帶組件, tag?: string | Class<Component> | Function | Object ,在下面可以看到

if (typeof tag === ‘string’){...   } 
else {
// direct component options / constructor
vnode = createComponent(tag, data, context, children)
}

/src/core/vdom/create-component.js 找到了createComponent ,這裡有三個主要的步驟 1. 繼承 Vue 原型並返回子類函數 2. 安裝組件鉤子函數 3. 實例化 Vnode

首先第一步 Ctor = baseCtor.extend(Ctor) 這裡會把組件做繼承, baseCtor 在 /src/core/global-api/extend.js 有繼承的定義,裡面將組件轉換為 Vue 原型的子類別並對其擴展與初始化函數。

第二步是 installComponentHooks 把鉤子函數放上去。

最後實例化 new Vnode 並返回一個沒有 children 的 Vnode。

2. 組件的 patch

上一篇提到 createComponent 或是 createElement 生成 Vnode 後會執行 update ,主要是使用 patch 這個方法來比對新舊 node 來 re-render 畫面, patch 原理參考的是 snabbdom 的 diff 演算法,至於 diff 演算法是什麼在這裡就不詳細說明了,組件的 patch 定義在src/core/vdom/patch.js 中有一個 createComponent function ,這 function 內的步驟是 1. 初始化鉤子函數 2. 插入 component 內節點 3. 比較節點並 reactivateComponent 重新渲染組件 。

在第一步時會調用 /src/core/vdom/create-component.js 內的 init 方法,在裡面有 createComponentInstanceForVnode ,這個方法是用來實例化組件的,之後對掃過每個 node 並進行 patch ,diff 之後會 insert 新的節點至組件並重新渲染。

3. 生命週期

先跳過 merge option 來看看生命週期,對於這張圖大家都不陌生。

/src/core/instance/lifecycle.js 找到定義方法,從 initLifecycle 開始,初始化所有鉤子參數

vm._watcher = null  
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false

所有鉤子函數都是靠最下面的 callHook 去呼叫

beforeCreate & created

這兩個是在 /src/core/instance/init.js 中 callHook

initLifecycle(vm)    
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')

看到在 beforeCreate 前分別初始化了生命週期、事件函數與渲染函數,之後開始 beforeCreate , initState 初始化了 data、 props、methods、computed等屬性。

beforeMount & mounted

回到 /src/core/instance/lifecycle.js 中的 mountComponent ,beforeMount 後就是上一章提到的 render 與 update 流程。

beforeUpdate & updated

update 靠著 Watcher 去監聽數據變化, /src/core/observer/watcher.js 可以找到此方法,裡面定義了 getter 去蒐集要監聽的數據再觸發 update。

beforeDestroy & destroyed

/src/core/instance/lifecycle.js 中定義了 destroyed,beforeDestroy 刪除了watcher、vnode等,最後 destroyed 再關閉所有監聽事件。

activated & deactivated

這是給 keep-alive 用的,之後再說明此鉤子如何作用。

總結

結論一下,createComponent 是創造一個 Vue 實例的子類,再去將安裝鉤子函數與實例,patch 參考 snabbdom 的 diff 演算法去做比較 Vnode 並 re-render ,生命週期定義在 /src/core/instance/lifecycle.js ,裡面用 callHook 方法調用各鉤子。

--

--