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)文章
Vue.js每天必學(xué)之指令系統(tǒng)與自定義指令
Vue.js每天必學(xué)之指令系統(tǒng)與自定義指令,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09
Vue實現(xiàn)點擊按鈕復(fù)制文本內(nèi)容的例子
今天小編就為大家分享一篇Vue實現(xiàn)點擊按鈕復(fù)制文本內(nèi)容的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11
在Vue 3中使用OpenLayers讀取WKB數(shù)據(jù)并顯示圖形效果
WKB作為一種緊湊的二進制格式,在處理和傳輸空間數(shù)據(jù)時具有明顯優(yōu)勢,本文介紹了如何在Vue 3中使用OpenLayers讀取WKB格式的空間數(shù)據(jù)并顯示圖形,感興趣的朋友一起看看吧2024-12-12
uniapp中使用lottie實現(xiàn)JSON動畫的操作步驟
這篇文章主要介紹了如何在項目中使用JSON動畫組件,包括創(chuàng)建目錄結(jié)構(gòu)、下載JSON文件、編寫自定義組件代碼以及組件的使用方法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2025-01-01
Vue3+Vite+TS實現(xiàn)二次封裝element-plus業(yè)務(wù)組件sfasga
這篇文章主要介紹了在Vue3+Vite+TS的基礎(chǔ)上實現(xiàn)二次封裝element-plus業(yè)務(wù)組件sfasga,下面文章也將圍繞實現(xiàn)二次封裝element-plus業(yè)務(wù)組件sfasga的相關(guān)介紹展開相關(guān)內(nèi)容,具有一定的參考價值,需要的小伙伴可惡意參考一下2021-12-12
Vue axios庫避免重復(fù)發(fā)送請求的示例介紹
Axios是一個基于promise的HTTP庫,可以用在瀏覽器和node.js中。axios是目前最優(yōu)秀的HTTP請求庫之一,我們封裝axios請求也是為了讓代碼看的更加清晰,后期好維護2023-02-02

