簡單介紹下 Vue 2.x 中的幾種生命周期鉤子(Lifecycle Hooks)
〇、前言
在使用 Element UI 框架(基于 Vue 2.x)開發應用時,理解 Vue 的生命周期鉤子(Lifecycle Hooks)是非常重要的。
這些鉤子函數可以在組件的不同階段執行特定的邏輯操作,從而避免因頁面數據加載順序不及預期而造成的異常。
本文就結合 Vue 2.x 版本簡單列一下都有哪些鉤子。
一、都有哪幾種生命周期鉤子
每個 Vue 組件實例在創建時都需要經歷一系列的初始化步驟,比如設置好數據偵聽,編譯模板,掛載實例到 DOM,以及在數據改變時更新 DOM。
在此過程中,它也會運行被稱為生命周期鉤子的函數,讓開發者有機會在特定階段運行自己的代碼。
下面是實例生命周期的圖表。

| 鉤子 | 是否可訪問 data | 是否可以訪問 DOM | 常見用途 | 使用建議 |
| beforeCreate | - | - | 初始化前,極少使用 | - |
| created | √ | - | 發送請求、初始化數據 | 在此階段獲取數據,避免阻塞渲染 |
| beforeMount | √ | - | DOM 掛載前準備 | - |
| mounted | √ | √ | DOM 操作、第三方庫初始化 | 在此階段處理 DOM 或初始化插件(如地圖、圖表) |
| beforeUpdate | √ | - | 數據發生變更,DOM 需要更新之前觸發 | - |
| updated | √ | √ | 數據變更后,DOM 更新完畢時觸發 | 在此階段對 DOM 做動態調整(如自動聚焦、布局重算) |
| beforeDestroy | √ | √ | 清理資源(定時器、事件監聽) | 在此階段中清理占內存比較多的變量,避免泄露 |
| destroyed | - | - | 執行那些不需要訪問組件內部數據或DOM的操作 | 例如,發送日志、通知外部系統組件已經被銷毀等 |
參考:https://cn.vuejs.org/guide/essentials/lifecycle
二、簡介
2.1 beforeCreate:實例初始化之后立即執行
beforeCreate 是在整個 Vue 實例初始化過程中非常早期的一個階段被調用。
beforeCreate 是 Vue 生命周期中調用的第一個鉤子。
觸發時機是在實例初始化之后,數據觀測(data observer)和事件/偵聽器配置之前被調用。
在 beforeCreate 鉤子中,Vue 實例尚未完成初始化,這意味著此時還無法訪問或操作實例中的 data 屬性、計算屬性(computed)、方法(methods)等。
此時 Vue 尚未開始編譯模板,因此也無法訪問 $el 或者通過 $refs 訪問 DOM 元素。
由于以上兩個原因,beforeCreate 就變得極不常用。
但是存在就有它的用途,例如:
- 執行不依賴于 Vue 實例狀態的操作:例如設置全局事件監聽器,雖然這種情況較為少見。
- 集成第三方庫:有時可能需要在 Vue 實例完全初始化之前做一些準備工作,比如與外部腳本或庫的交互。
在多數情況下,更推薦于使用 created 來代替 beforeCreate,因為 created 提供了對數據和方法的訪問權限。
2.2 created:完成了數據觀測(data observer)、屬性和方法的運算之后
created 生命周期鉤子是一個非常重要的階段,它標志著 Vue 實例已經完成了數據觀測(data observer)、屬性和方法的運算,但尚未開始模板編譯,也未掛載到 DOM 上。
此階段的特點:
- 可以訪問到 data、methods、computed 等屬性。
- 此時組件還沒有被掛載到頁面上,所以不能訪問或操作 $el 或者通過 $refs 訪問 DOM 元素。
- 因為此階段可以訪問到組件的數據和方法,因此它是發起異步請求(如 API 請求)來獲取數據的好地方。
- 可以在此階段進行一些初始化工作,比如監聽某些事件,或者調用某些方法來初始化組件狀態。
created 階段可以干什么?例如:
- 數據初始化:從服務器獲取數據并填充到組件的數據屬性中。
- 事件監聽:添加全局或局部的事件監聽器。
- 第三方庫集成:如果需要與第三方 JavaScript 庫集成,可以在 created 鉤子中進行必要的初始化工作。
雖然 created 鉤子可以執行許多初始化任務,但它并不適合執行復雜的計算或大規模的操作,因為這些可能會導致性能問題。
created 鉤子是在實例創建完成之后立刻調用,而 mounted 是在實例掛載到 DOM 之后調用。如果你需要直接操作 DOM,應該使用 mounted 而不是 created。
在 Element UI 中,可以在 created 階段請求數據并綁定給 tableData,用于后續渲染 <el-table>。
如下一個簡單的示例:
new Vue({
el: '#app',
data() {
return {
message: 'Hello Vue!',
posts: []
}
},
created() {
console.log('created hook called');
// 在這里可以訪問到 this.message 和其他 data 屬性
console.log(this.message); // 輸出 "Hello Vue!"
// 發起異步請求加載數據
this.fetchPosts();
},
methods: {
fetchPosts() {
// 模擬異步請求
setTimeout(() => {
this.posts = [
{ id: 1, title: 'Vue.js 基礎' },
{ id: 2, title: '深入理解 Vue 組件' }
];
}, 1000);
}
}
});
2.3 beforeMount:模板已編譯完成,但未掛載到頁面上
觸發時機是實例掛載之前,即模板編譯完成,但尚未將渲染好的 DOM 掛載到頁面上時觸發。
DOM 尚未生成。Vue 已經完成了模板編譯,但是還沒有將虛擬 DOM 渲染并插入到實際的文檔中。因此,無法訪問或操作真實的 DOM 元素。
數據已經響應式處理。此時組件的數據已經被設置為響應式,這意味著你可以訪問 data 和 methods 等屬性,但還不能對真實 DOM 做任何修改。
極少直接使用。因為其特殊的位置,beforeMount 并不是特別常用。大多數情況下,開發者更傾向于在 mounted 鉤子里執行需要與 DOM 相關的操作。
盡管 beforeMount 的應用場景有限,但在某些特定的情況下仍然有用:
- 準備即將掛載的組件:如果需要在組件掛載前做一些準備工作,比如設置一些狀態或預加載資源,可以考慮在此階段進行。
- 性能監控:用于記錄組件從創建到掛載的時間點,幫助分析應用的性能瓶頸。
new Vue({
el: '#app',
data() {
return {
message: 'Hello Vue!'
}
},
beforeMount() {
console.log('beforeMount hook called');
// 注意:這里可以訪問 this.message,但是不能訪問真實的 DOM 元素
console.log(this.$el); // undefined 或者即將被替換的原始元素
},
mounted() {
console.log('mounted hook called');
// 此時可以訪問真實的 DOM 元素
console.log(this.$el); // 實際的 DOM 節點
}
});
2.4 mounted:編譯已完成,且已將渲染好的 DOM 掛載到頁面上
當 mounted 鉤子被調用時,說明組件已經被插入到 DOM 中,所有的子組件也都已經掛載完成。這是整個生命周期中最先能夠訪問或操作 DOM 的階段。
可進行的操作:
- DOM 操作:由于在這個鉤子內可以訪問到真實的 DOM 元素,因此非常適合進行那些需要直接操作 DOM 的工作,比如初始化第三方庫(如地圖、圖表等),或者手動設置一些樣式。
- 異步數據獲取:雖然也可以在 created 鉤子中進行數據獲取,但由于此時 DOM 尚未準備好,如果你的數據獲取邏輯依賴于特定的 DOM 狀態,則應考慮在 mounted 中執行。
- 事件監聽器的添加:此階段還可以給非 Vue 管理的 DOM 元素添加事件監聽器。
如果在 mounted 鉤子中添加了任何全局或第三方的資源引用(如事件監聽器或定時器),請確保在組件銷毀之前(通常是在 beforeDestroy 或 destroyed 鉤子中)進行適當的清理,以防止內存泄漏。
在 Vue 2.0 中,mounted 鉤子提供了一個理想的切入點,可以在組件掛載完成后立即執行必要的操作,特別是那些涉及到 DOM 操作和外部庫集成的場景。正確利用它可以極大地提高應用的靈活性和功能豐富度。
在 Element UI 中,可以實現對 <el-select> 下拉框進行手動聚焦,初始化 ECharts 圖表等。
2.5 beforeUpdate:已發現 DOM 樹的變更點,著手更新前調用此回調
當 Vue 檢測到組件中的響應式數據發生了變化,并且這些變化導致了虛擬 DOM(Virtual DOM)的重新渲染時,Vue 會先進行一次 diff 算法來比較新舊虛擬 DOM 樹的不同之處。在確定了需要更新的實際 DOM 后,Vue 將會執行必要的 DOM 操作以反映數據的變化。就在此執行 DOM 操作之前,beforeUpdate 鉤子會被觸發。這個鉤子允許在組件的 DOM 更新前執行自定義邏輯。
可以進行哪些操作:
- 訪問當前 DOM 狀態:如果你想在更新前 訪問或檢查當前的 DOM 元素狀態,例如獲取某些元素的尺寸、位置等信息,可以在 beforeUpdate 鉤子中進行。
- 手動清理或移除事件監聽器:如果你在組件內動態添加了事件監聽器或者其他需要手動清理的資源,在這里可以進行相應的清理工作,以避免不必要的內存泄漏或者副作用。
- 性能監控與調試:可以通過 beforeUpdate 來記錄組件更新的頻率、查看數據變化情況,有助于性能優化和問題定位。
在 beforeUpdate 鉤子內部,DOM 元素是上一次更新的結果,即還沒有應用新的數據更改到 DOM 上的狀態。因此,如果想要根據最新的數據狀態來操作 DOM,應該考慮使用 updated 鉤子。
不建議在 beforeUpdate 鉤子中改變組件的狀態(如修改 data 屬性),因為這可能會導致無限循環更新的問題,既然已經進入了更新周期,嘗試再次觸發更新會導致遞歸調用,影響性能甚至引發錯誤。
通過 beforeUpdate 生命周期鉤子,可以組件的行為更加精細化的控制,特別是在處理復雜的用戶交互或需要精細調整的界面更新邏輯時顯得尤為重要。
2.6 updated:組件因為響應式數據的變化而更新其 DOM 結構之后被調用
與 beforeUpdate 不同,updated 則是在執行 DOM 操作完成之后才會觸發。
可以進行 DOM 更新后的操作。某些需要在 DOM 更新后執行某些操作,比如動畫控制、第三方庫的初始化或與服務器同步狀態等,就可以在 updated 鉤子中進行。
注意:由于數據變化可能導致視圖的多次重繪,在 updated 鉤子里要小心不要進行可能會再次觸發數據變化的操作,以免造成無限循環更新。
在 Element UI 中可以在表格數據更新后,重新調整列寬。
Vue 實現了一個異步更新隊列,這意味著當同時修改多個響應式數據時,Vue 會將它們合并為一次更新。因此,如果想要確保所有的更新都已經完成并反映到了 DOM 上,可以使用 this.$nextTick 方法,它會在 DOM 更新循環結束后執行回調函數。
下面是一個簡單的示例:
<template>
<div>
<p>當前計數值:{{ count }}</p>
<button @click="increment">增加計數</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0,
};
},
methods: {
increment() {
this.count++;
}
},
beforeUpdate() {
console.log('beforeUpdate 鉤子被調用');
console.log('當前 DOM 內容:', document.querySelector('p').innerText);
// 在這里可以訪問到舊的數據,但是對 DOM 的操作可能不會反映出來
},
updated() {
console.log('updated 鉤子被調用');
console.log('更新后的 DOM 內容:', document.querySelector('p').innerText);
// 組件已經更新完畢,可以在這里安全地操作更新后的 DOM
}
};
</script>
2.7 beforeDestroy:在整個 Vue 實例銷毀之前調用
這個階段,Vue 實例仍然完全可用,所有數據綁定、事件監聽器以及子組件都還處于完整可用的狀態,這意味著還可以執行一些操作,如清理工作、取消網絡請求或定時器等。
可以進行的操作:
- 資源清理:此階段是進行資源管理的最佳位置,比如清除定時器、取消尚未完成的網絡請求或者移除手動添加的 DOM 元素事件監聽器。
- 狀態保存:如果你的應用有需要在組件銷毀前保存的狀態(例如:用戶輸入但未提交的數據),可以在此階段進行處理。
- 解綁全局事件:如果在組件中通過 $on 或者直接使用 JavaScript 在全局對象上綁定了事件,應該在此處進行解綁,以避免內存泄漏。
一旦 beforeDestroy 鉤子中的代碼執行完畢,Vue 將開始卸載組件的過程,包括但不限于解除對父級組件的引用、銷毀所有子組件實例以及移除 DOM 元素。
例如在 Element UI 中,可以在此階段銷毀頁面時清除全局 loading 效果。
注意:在 Vue 3 中,beforeDestroy 被重命名為 beforeUnmount,以更好地反映其實際意義,即它是在組件卸載之前被調用,而不是真正的“銷毀”。
2.8 destoryed:在 Vue 實例被銷毀之后調用
在這個階段,Vue 實例的所有指令都被解綁,所有事件監聽器也被移除,所有的子實例也都被銷毀。這意味著組件已經完成了它的使命,并且從 DOM 中完全移除了。
可以進行的操作:
- 清理工作:比如你可能需要取消一些尚未完成的異步請求,或者移除手動添加的事件監聽器(不在 Vue 的事件系統中注冊的),以避免內存泄漏。
- 記錄或報告:可以用來記錄日志或者發送分析數據,表明某個組件已經被銷毀。
- 執行最后的操作:例如通知其他系統組件已銷毀等。
注意:如果組件間有依賴關系,需要確保在銷毀時,正確地處理這些依賴,以便它們也能得到適當的清理。
下面是一個簡單的示例:
new Vue({
el: '#example',
data: {
message: 'Hello Vue!'
},
methods: {
// 假設這里有一個定時器
startPolling: function() {
this.pollingTimer = setInterval(() => {
console.log(this.message);
}, 1000);
}
},
mounted: function() {
this.startPolling();
},
beforeDestroy() {
// 在組件銷毀前清除定時器
// 在頁面退出前清理定時器的最佳實踐就是在 beforeDestroy 中進行,因為這個時候組件還“活著”,可以確保所有的資源都被正確釋放
clearInterval(this.pollingTimer);
console.log('Before Destroy: Timer cleared.');
},
destroyed() {
// 組件已被銷毀后的操作
console.log('Destroyed: Component A has been removed.');
// 到了 destroyed 鉤子,組件已經不再活躍,如果定時器沒有在 beforeDestroy 中被清除,則可能已經導致了不必要的資源占用甚至內存泄漏
}
});
另外,在 Vue 3 中被重命名為 unmounted。
三、另外幾個基礎概念
- 什么是:數據觀測(Data Observer)?
Vue 將 data 函數返回的對象中的所有屬性,轉換為 getter/setter 形式,以便追蹤依賴并進行響應式更新。
- 什么是:屬性和方法運算?
Vue 對所有組件的屬性(props)、計算屬性(computed)以及方法(methods)進行解析,之后就可以在實例中訪問了。
- 什么是:事件監聽器設置?
將所有通過 events 或直接在模板上定義的事件監聽器設置完成。
- 什么是:模板編譯?
Vue 的模板編譯過程是將模板(HTML)轉換為渲染函數(render function)的過程,這個過程對于 Vue 組件的渲染至關重要。
編譯過程總共有三步:
- 模板解析(Template Parsing):在這一階段,Vue 會將頁面模板字符串解析成抽象語法樹(AST)。AST 是一種樹形結構,用來表示模板中的每一個元素、屬性及其關系。這一步驟使得 Vue 能夠理解和處理頁面模板代碼。
- 優化(Optimization):生成 AST 后,Vue 對其進行靜態分析以優化后續更新檢查的效率。Vue 會標記靜態節點和靜態根節點。靜態節點是指那些不會隨應用狀態變化而改變的節點。通過這種優化,Vue 可以跳過這些靜態部分,在重新渲染時只關注動態內容的變化,從而提升渲染性能。
- 代碼生成(Code Generation):最后一步是根據 AST 生成渲染函數(render function)。渲染函數是一個 JavaScript 函數,它描述了如何創建虛擬 DOM 來匹配模板。在 Vue 2 中,可以直接編寫渲染函數而不是使用模板;而在 Vue 3 中,即使使用的是單文件組件(SFC),最終也會被編譯成渲染函數。
渲染函數和虛擬 DOM:
生成的渲染函數會被用來創建虛擬 DOM 樹。虛擬 DOM 是一個輕量級的內存數據結構,它模仿真實的 DOM 樹結構。當組件的狀態發生變化時,Vue 會重新運行渲染函數以生成新的虛擬 DOM 樹,并通過差異算法(diffing algorithm)對比新舊兩棵樹的不同之處,僅對實際發生變更的部分進行必要的真實 DOM 更新。
本文來自博客園,作者:橙子家,歡迎微信掃碼關注博主【橙子家czzj】,有任何疑問歡迎溝通,共同成長!

浙公網安備 33010602011771號