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ī)則進(jìn)行合并。對于生命周期鉤子函數(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中的選項進(jìn)行合并:
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ìn)行合并,之后會在組件初始化時將其和組件實例化時傳入的選項根據(jù)不同的合并策略進(jìn)行合并,這樣會根據(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
// 將局部選項和全局選項進(jìn)行合并
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 的話,會采用默認(rèn)的合并策略defaultStrategy來處理默認(rèn)的合并邏輯。
這樣可以讓我們不用再用if else來不停為每一個選項進(jìn)行判斷,使代碼更加簡潔。并且在之后如果需要添加新的合并策略時,只需要添加類似如下代碼即可,更易于維護(hù):
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ù)進(jìn)行合并 - 封裝
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-09
Vue 實現(xiàn)分頁與輸入框關(guān)鍵字篩選功能
這篇文章主要介紹了Vue 實現(xiàn)分頁+輸入框關(guān)鍵字篩選功能,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-01-01
詳解vue-cli4 配置不同開發(fā)環(huán)境打包命令
這篇文章主要介紹了vue-cli4 配置不同開發(fā)環(huán)境打包命令,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07

