Vue生命周期函數(shù)調(diào)用詳解
生命周期
Vue
為用戶提供了許多生命周期鉤子函數(shù),可以讓用戶在組件運行的不同階段書寫自己的邏輯。
那么Vue
內(nèi)部到底是如何處理生命周期函數(shù)的呢?Vue
的生命周期究竟是在代碼運行的哪個階段執(zhí)行呢?本文將實現(xiàn)Vue
生命周期相關(guān)代碼的核心邏輯,從源碼層面來理解生命周期。
Vue.mixin
在介紹生命周期之前,我們先來看下Vue.mixin
。
Vue.mixin
是Vue
的全局混合器,它影響Vue
創(chuàng)建的每一個實例,會將mixin
中傳入的配置項與組件實例化時的配置項按照一定規(guī)則進行合并。對于生命周期鉤子函數(shù),相同名字的生命周期將會合并到一個數(shù)組中,混合器中的鉤子函數(shù)將會先于組件中的鉤子函數(shù)放入到數(shù)組中。在特定時機時,從左到右執(zhí)行數(shù)組中的每一個鉤子函數(shù)。
<div id="app"> </div> <script> // 生命周期: Vue.mixin({ created () { console.log('global created'); } }); const vm = new Vue({ el: '#app', data () { }, created () { console.log('component created'); } }); // global created // component created </script>
上述代碼會先執(zhí)行Vue.mixin
中的created
函數(shù),然后再執(zhí)行組件中的created
函數(shù)。下面我們看下Vue.mixin
是怎么實現(xiàn)。
// src/global-api/index.js import mergeOptions from '../shared/merge-options'; export function initGlobalApi (Vue) { Vue.options = {}; Vue.mixin = function (mixin) { this.options = mergeOptions(this.options, mixin); }; } // src/index.js function Vue (options) { this._init(options); } // 初始化全局api initGlobalApi(Vue); export default Vue;
在scr/index.js
中執(zhí)行initGlobalApi
方法,會為Vue
添加options
和Vue.mixin
屬性。
Vue.mixin
會將調(diào)用該函數(shù)時傳入的配置項與Vue.options
中的選項進行合并:
Vue.options = {}; Vue.mixin = function (mixin) { // Vue.options = mergeOptions(Vue.options, mixin) this.options = mergeOptions(this.options, mixin); };
Vue.options
中會保存所有全局的配置項,如components,directives
等。執(zhí)行Vue.mixin
之后,Vue.options
會和Vue.mixin
中的選項進行合并,之后會在組件初始化時將其和組件實例化時傳入的選項根據(jù)不同的合并策略進行合并,這樣會根據(jù)最終合并后的全局選項和組件選項來創(chuàng)建Vue
實例:
// src/init.js function initMixin (Vue) { Vue.prototype._init = function (options) { const vm = this; // 在初始化根組件時: vm.constructor.options -> Vue.options // 在初始化子組件時: vm.constructor.options -> Sub.options // 將局部選項和全局選項進行合并 vm.$options = mergeOptions(vm.constructor.options, options); // some code ... }; // some code ... }
現(xiàn)在關(guān)鍵邏輯來到了mergeOptions
,下面來介紹mergeOptions
的整體編寫思路以及生命周期的合并過程。
生命周期選項合并
mergeOptions
函數(shù)完成了組件中選項合并的邏輯:
const strategies = {}; function defaultStrategy (parentVal, childVal) { return childVal === undefined ? parentVal : childVal; } const LIFECYCLE_HOOKS = [ 'beforeCreate', 'created', 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'beforeDestroy', 'destroyed' ]; function mergeHook (parentVal, childVal) { if (parentVal) { if (childVal) { // concat可以拼接值和數(shù)組,但是相對于push來說,會返回拼接后新數(shù)組,不會改變原數(shù)組 return parentVal.concat(childVal); } return parentVal; } else { return [childVal]; } } LIFECYCLE_HOOKS.forEach(hook => { strategies[hook] = mergeHook; }); function mergeOptions (parent, child) { // 將子選項和父選項合并 const options = {}; function mergeField (key) { const strategy = strategies[key] || defaultStrategy; options[key] = strategy(parent[key], child[key]); } for (const key in parent) { if (parent.hasOwnProperty(key)) { mergeField(key); } } for (const key in child) { if (child.hasOwnProperty(key) && !parent.hasOwnProperty(key)) { mergeField(key); } } return options; } export default mergeOptions;
對于不同的選項,Vue
會采取不同的合并策略。也就是為strategies
添加Vue
的各個選項作為key
,其對應(yīng)的合并邏輯是一個函數(shù),為strategies[key]
的值。如果沒有對應(yīng)key
的話,會采用默認的合并策略defaultStrategy
來處理默認的合并邏輯。
這樣可以讓我們不用再用if else
來不停為每一個選項進行判斷,使代碼更加簡潔。并且在之后如果需要添加新的合并策略時,只需要添加類似如下代碼即可,更易于維護:
function mergeXXX (parentVal, childVal) { return result } strategies[xxx] = mergeXXX
對于生命周期,我們會將每個鉤子函數(shù)都通過mergeHook
合并為一個數(shù)組:
function mergeHook (parentVal, childVal) { if (parentVal) { if (childVal) { // concat可以拼接值和數(shù)組,但是相對于push來說,會返回拼接后新數(shù)組,不會改變原數(shù)組 return parentVal.concat(childVal); } return parentVal; } else { return [childVal]; } }
在Vue.mixin
中提到例子的合并結(jié)果如下:
現(xiàn)在我們已經(jīng)成功將生命周期處理成了數(shù)組,接下來便到了執(zhí)行數(shù)組中的所有鉤子函數(shù)的邏輯了。
調(diào)用生命周期函數(shù)
完成上述代碼后,我們已經(jīng)成功將所有合并后的生命周期放到了vm.$options
中對應(yīng)的生命周期數(shù)組中:
vm.$options = { created: [f1, f2, f3], mounted: [f4, f5, f6] // ... }
想要執(zhí)行某個生命周期函數(shù),可以用它的名字從vm.$options
找到其對應(yīng)的函數(shù)執(zhí)行。為了方便生命周期的調(diào)用,封裝了一個callHook
函數(shù)來幫我們做這些操作:
// src/lifecycle.js export function callHook (vm, hook) { const handlers = vm.$options[hook]; if (handlers) { handlers.forEach(handler => handler.call(vm)); } }
對于目前我們已經(jīng)完成的代碼,可以在如下位置添加生命周期鉤子函數(shù)的調(diào)用:
此時,用戶在使用時傳入的beforeCreate,created,beforeMount,Mounted
鉤子函數(shù)就可以正確執(zhí)行了。
beforeCreate
:在組件初始化狀態(tài)initState
之前執(zhí)行,此時不能訪問props,methods,data,computed
等實例上的屬性created
:組件初始化狀態(tài)后執(zhí)行,此時props,methods,data
等選項已經(jīng)初始化完畢,可以通過實例來直接訪問beforeMount
: 組件過載之前執(zhí)行mounted
: 組件掛載之后執(zhí)行,即使用實例上最新的data
生成虛擬DOM
,然后將虛擬DOM
掛載到真實DOM
之后執(zhí)行。
結(jié)語
生命周期函數(shù)本質(zhì)上就是我們在配置項中傳入回調(diào)函數(shù),Vue
會將我們傳入的配置項收集到數(shù)組中,然后在特定時機統(tǒng)一執(zhí)行。
Vue
的生命周期從定義到執(zhí)行一共經(jīng)歷了如下幾個步驟:
- 在組件實例化時作為選項傳入
- 首先將
Vue.mixin
中傳入的配置項和Vue.options
中的生命周期函數(shù)合并為一個數(shù)組 - 將組件實例化時傳入的選項和
Vue.options
中的生命周期繼續(xù)進行合并 - 封裝
callHook
函數(shù),從vm.$options
中找到指定生命周期函數(shù)對應(yīng)的數(shù)組 - 在特定時機執(zhí)行特定的生命周期函數(shù)
到此這篇關(guān)于Vue生命周期函數(shù)調(diào)用詳解的文章就介紹到這了,更多相關(guān)Vue生命周期內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
源碼地址:傳送門
相關(guān)文章
vuex 中輔助函數(shù)mapGetters的基本用法詳解
mapGetters輔助函數(shù)僅僅是將 store 中的 getter 映射到局部計算屬性,在組件或界面中不使用mapGetter調(diào)用映射vuex中的getter,在組件或界面中使用mapGetter調(diào)用映射vuex中的getter,具體內(nèi)容跟隨小編一起通過本文學(xué)習(xí)吧2021-07-07關(guān)于vue項目中搜索節(jié)流的實現(xiàn)代碼
這篇文章主要介紹了關(guān)于vue項目中搜索節(jié)流的實現(xiàn)代碼,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-09-09Vue 實現(xiàn)分頁與輸入框關(guān)鍵字篩選功能
這篇文章主要介紹了Vue 實現(xiàn)分頁+輸入框關(guān)鍵字篩選功能,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2020-01-01詳解vue-cli4 配置不同開發(fā)環(huán)境打包命令
這篇文章主要介紹了vue-cli4 配置不同開發(fā)環(huán)境打包命令,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07