組件生成與生命週期
組件化是 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
- 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 方法調用各鉤子。