1.vue基本生命周期
vue源碼中最終執行生命周期函數都是調用callHook
方法,callHook
函數的邏輯很簡單,根據傳入的生命周期類型 hook
,去拿到 vm.$options[hook]
對應的回調函數數組,然后遍歷執行,執行的時候把 vm
作為函數執行的上下文。
1. new Vue(options)
:創建一個vm實例;
2. mergeOptions(resolveConstructorOptions(vm.constructor), options, vm)
:合并Vue構造函數里options和傳入的options或合并父子的options。比如:在mergeOptions函數中會調用mergeHook方法合并生命周期的鉤子函數,mergeHook方法原理是只有父時返回父,只有子時返回數組類型的子。父、子都存在時,將子添加在父的后面返回組合而成的數組。這也是父子均有鉤子函數的時候,先執行父的后執行子的的原因;
3. initLifecycle(vm)、initEvents(vm)、initRender(vm)
:在創建的vm實例上初始化生命周期、事件、渲染相關的屬性;
4. callHook(vm, 'beforeCreate')
:調用beforeCreate生命周期鉤子函數;
5. initInjections(vm)、initState(vm)、initProvide(vm)
:初始化數據:inject、state、provide。initState 的作用是初始化 props、data、methods、watch、computed 等屬性;
6. callHook(vm, 'created')
:調用created生命周期鉤子函數;
7. vm.$mount(vm.$options.el)
:$mount
方法在多個文件中都有定義,如"src/platform/web/entry-runtime-with-compiler.js"、"src/platform/web/runtime/index.js"、"src/platform/weex/runtime/index.js"。因為$mount
方法的實現是和平臺、構建方式相關的。以"entry-runtime-with-compiler.js"為例,關鍵步驟是查看vm.$options
中是否有render方法,如果沒有則會根據el和template屬性確定最終的template字符串,再調用compileToFunctions
方法將template字符串轉為render方法,最后,調用原先原型上的$mount方法,即開始執行"lifecycle.js"中mountComponent
方法;
8. callHook(vm, 'beforeMount')
:調用beforeMount生命周期鉤子函數;
9. vm._render()
=> vm._update()
=> vm.__patch__()
:先執行vm._render方法,即調用createElement生成虛擬DOM,即VNode ,每個VNode有children ,children 每個元素也是?個 VNode,這樣就形成了?個 VNode Tree;再調用vm._update方法進行首次渲染,vm._update方法核心是調用vm.patch方法,這個方法跟vm.$mount一樣跟平臺相關;vm.patch方法則是根據生成的VNode Tree遞歸createElm方法創建真實Dom Tree掛載到Dom上;
10. callHook(vm, 'mount')
:調用mount生命周期鉤子函數:VNode patch 到 Dom 之后會執行 'invokeInsertHook'函數,把insertedVnodeQueue
中保存的mount鉤子函數執行一遍,insertedVnodeQueue隊列中的鉤子函數是在根據VNode Tree遞歸createElm方法創建真實Dom Tree過程生成的鉤子函數順序隊列,因此mounted鉤子函數的執行順序是先子后父;
11. data changes
:數據更新,nextTick中執行flushSchedulerQueue
方法,該方法會執行watcher隊列中的watcher;
12. callHook(vm, 'beforeUpdate')
:執行watcher時會執行watcher的before方法,即調用beforeUpdate生命周期鉤子函數;
13. Virtual DOM re-render and patch
:重新render生成新的Virtual DOM,并且patch到DOM上;
14. callHook(vm, 'updated')
:調用updated生命周期鉤子函數;
15. vm.$destroy()
:啟動卸銷毀過程;
16. callHook(vm, 'beforeDestroy')
:調用beforeDestroy生命周期鉤子函數;
17. Teardown watchers, childcomponents and event listeners
:執行一系列銷毀動作,在 $destroy 的執行過程中,它又會執行vm.__patch__(vm._vnode, null)
觸發它子組件的銷毀鉤子函數,這樣一層層的遞歸調用,所以 destroyed 鉤子函數執行順序是先子后父,和 mounted 過程一樣。
18. callHook(vm, 'destroyed ')
:調用destroyed 生命周期鉤子函數。
2. errorCaptured
生命周期鉤子函數
當捕獲一個來自子孫組件的錯誤時被調用。此鉤子會收到三個參數:錯誤對象、發生錯誤的組件實例以及一個包含錯誤來源信息的字符串。此鉤子可以返回 false 以阻止該錯誤繼續向上傳播。
如果一個組件的繼承或父級從屬鏈路中存在多個 errorCaptured 鉤子,則它們將會被相同的錯誤逐個喚起。
默認情況下,如果全局的 config.errorHandler 被定義,所有的錯誤仍會發送它,因此這些錯誤仍然會向單一的分析服務的地方進行匯報。
3. keep-alive組件
keep-alive組件是vue的內置抽象(abstract)組件,抽象組件在initLifecycle過程中 組件實例建立父子關系時會被忽略,因此他自身不會渲染一個DOM元素,也不會出現在父組件鏈中。
keep-alive作用是用于包裹動態組件,緩存不活動的組件實例。keep-alive自定義實現了render函數并利用了插槽slot,render函數中先獲取它的默認插槽,再獲取到它的第一個組件子節點,因此keep-alive組件只處理第一個子元素,所以一般和它搭配使用的有component動態組件或者router-view。
keep-alive組件在created鉤子中定義了 this.cache 和 this.keys,本質上是去緩存已創建的 vnode,緩存策略采用LRU策略,每次緩存命中時將當前vnode移到緩存數組末尾,需要刪除時則刪除緩存數組第一個vnode。
-
keep-alive組件接收三個props:
1.
include
:數組、字符串或者正則表達式,只有匹配的組件才會緩存。2.
exclude
:數組、字符串或者正則表達式,任何匹配的組件都不會被緩存。3.
max
:字符串或數字,指定可以緩存的組件最大個數,如果個數超過max,則銷毀緩存數組中的第一個組件。 -
keep-alive組件子組件渲染機制:
1. 首次渲染:和普通組件一樣執行正常的init生命周期鉤子函數,同時將生成的vnode緩存到內存中;
2. 組件切換:切換到新組件時,舊組件不會銷毀,而是變成未激活狀態,即不會執行destroy相關的鉤子函數,而是執行
deactivated
生命周期鉤子函數,如果新組件不在緩存數組中,則執行首次渲染,否則執行緩存渲染;3. 緩存渲染:緩存渲染即組件由未激活狀態變成激活狀態,因此不會執行created、mounted等鉤子函數,而是執行
activated
生命周期鉤子函數。