Vue框架之生命周期主要鉤子函數詳解
- 一、Vue生命周期的整體流程
- 二、創建階段:初始化組件實例
- 2.1 `beforeCreate`:實例創建前
- 2.2 `created`:實例創建后
- 三、掛載階段:組件與DOM結合
- 3.1 `beforeMount`:掛載前
- 3.2 `mounted`:掛載后
- 四、更新階段:數據變化觸發重渲染
- 4.1 `beforeUpdate`:更新前
- 4.2 `updated`:更新后
- 五、銷毀階段:組件實例的清理
- 5.1 `beforeDestroy`:銷毀前
- 5.2 `destroyed`:銷毀后
- 六、特殊場景的鉤子函數
- 6.1 keep-alive 相關鉤子
- 6.2 錯誤捕獲鉤子:`errorCaptured`
- 七、生命周期鉤子的執行順序與實戰示例
- 7.1 單組件執行順序
- 7.2 父子組件執行順序
- 八、常見問題與避坑指南
- 8.1 避免在`updated`中修改數據
- 8.2 子組件`mounted`晚于父組件`mounted`
- 8.3 清理資源是重中之重
- 九、Vue 3中的生命周期變化
Vue的生命周期是指組件從創建到銷毀的整個過程,而生命周期鉤子函數則是在這個過程中特定時間點自動執行的函數。掌握這些鉤子函數,能讓我們在合適的時機執行特定操作(如數據請求、DOM操作、資源清理等),是Vue開發的核心基礎。
一、Vue生命周期的整體流程
Vue組件的生命周期可分為4個階段,每個階段包含若干鉤子函數,整體流程如下:
- 創建階段:組件實例從初始化到掛載前的過程
- 掛載階段:組件實例掛載到DOM的過程
- 更新階段:組件數據變化導致重新渲染的過程
- 銷毀階段:組件實例從DOM中移除并清理資源的過程
二、創建階段:初始化組件實例
創建階段是組件實例從無到有的過程,主要完成數據觀測(響應式處理)、事件初始化等工作,此時尚未涉及DOM操作。
2.1 beforeCreate
:實例創建前
- 執行時機:Vue實例初始化后(
new Vue()
之后),數據觀測(data
、props
)和事件機制初始化前調用。 - 特點:
- 無法訪問
data
、props
、methods
中的數據和方法(此時尚未初始化)。 - 不能進行DOM操作(DOM尚未生成)。
- 無法訪問
- 適用場景:極少使用,可用于初始化非響應式數據(如臨時變量)。
new Vue({data() {return { message: 'Hello' };},beforeCreate() {console.log('beforeCreate:', this.message); // undefined(無法訪問data)console.log('methods:', this.getMsg); // undefined(無法訪問methods)},methods: {getMsg() { return this.message; }}
});
2.2 created
:實例創建后
- 執行時機:Vue實例初始化完成后調用,此時已完成
data
、props
的響應式處理和methods
的綁定,但尚未開始DOM編譯(模板未掛載到DOM)。 - 特點:
- 可訪問
data
、props
、methods
中的數據和方法。 - 仍無法進行DOM操作(
$el
屬性不存在,DOM未生成)。
- 可訪問
- 適用場景:
- 發起初始化數據請求(如獲取列表數據)。
- 初始化數據(如對
data
中的數據進行預處理)。 - 綁定自定義事件。
new Vue({el: '#app',data() {return { list: [] };},created() {console.log('created:', this.list); // [](可訪問data)console.log('$el:', this.$el); // undefined(DOM未掛載)// 示例:發起數據請求axios.get('/api/list').then(response => {this.list = response.data; // 數據響應后更新list(響應式)});}
});
三、掛載階段:組件與DOM結合
掛載階段是組件實例與DOM關聯的過程,核心是將編譯后的模板掛載到頁面中,此時開始具備DOM操作能力。
3.1 beforeMount
:掛載前
- 執行時機:模板編譯(解析指令、插值表達式等)完成后,DOM掛載到頁面之前調用。
- 特點:
- 已完成模板編譯,生成虛擬DOM(但未渲染到頁面)。
$el
屬性存在(指向即將掛載的DOM元素),但內容仍為原始模板(未替換數據)。- 仍無法操作DOM(數據未渲染,操作無意義)。
- 適用場景:極少使用,可用于獲取模板編譯前的DOM結構。
new Vue({el: '#app',template: '<div>{{ message }}</div>',data() { return { message: '掛載前' }; },beforeMount() {console.log('beforeMount $el:', this.$el); // <div>{{ message }}</div>(未替換)console.log('頁面內容:', document.getElementById('app').innerHTML); // 原始模板}
});
3.2 mounted
:掛載后
- 執行時機:模板掛載到DOM后調用,此時頁面已顯示渲染后的內容。
- 特點:
- DOM已完全渲染,可通過
$el
或原生DOM API(如document.getElementById
)操作DOM。 - 子組件的
mounted
可能在父組件的mounted
之后執行(因渲染順序)。
- DOM已完全渲染,可通過
- 適用場景:
- 執行DOM操作(如初始化第三方UI插件:圖表、地圖等,需依賴DOM元素)。
- 監聽DOM事件(如滾動、resize)。
- 若數據請求依賴DOM尺寸(如根據容器寬度請求不同數據),可在此發起請求。
new Vue({el: '#app',data() { return { width: 0 }; },mounted() {// 獲取DOM元素寬度this.width = this.$el.offsetWidth;console.log('掛載后寬度:', this.width);// 初始化第三方圖表插件(假設頁面有<div id="chart"></div>)this.chart = new Chart(document.getElementById('chart'), {type: 'line',data: { labels: ['1月', '2月'], datasets: [{ data: [10, 20] }] }});// 監聽滾動事件window.addEventListener('scroll', this.handleScroll);},methods: {handleScroll() { /* 處理滾動邏輯 */ }}
});
四、更新階段:數據變化觸發重渲染
當組件的data
或props
發生變化時,會進入更新階段,觸發重新渲染,此階段的鉤子函數用于監控或干預更新過程。
4.1 beforeUpdate
:更新前
- 執行時機:數據發生變化后,虛擬DOM重新渲染前調用。
- 特點:
- 此時
data
中的數據已更新,但DOM尚未重新渲染(頁面顯示舊數據)。 - 可獲取更新前的DOM狀態。
- 此時
- 適用場景:獲取更新前的DOM信息(如滾動位置、輸入框光標位置),用于更新后恢復狀態。
new Vue({el: '#app',data() { return { count: 0 }; },template: '<div>{{ count }} <button @click="count++">+1</button></div>',beforeUpdate() {console.log('更新前data:', this.count); // 新值(如1)console.log('更新前DOM:', this.$el.textContent); // 舊值(如0)}
});
4.2 updated
:更新后
- 執行時機:虛擬DOM重新渲染并更新到頁面后調用,此時頁面顯示最新數據。
- 特點:
data
和DOM均已更新,可獲取最新的DOM狀態。- 若在
updated
中修改data
,會再次觸發更新(可能導致無限循環,需避免)。
- 適用場景:
- 基于最新DOM狀態執行操作(如根據新內容調整元素樣式)。
- 同步第三方插件數據(如圖表數據更新后,重新繪制圖表)。
new Vue({el: '#app',data() { return { data: [10, 20] }; },template: '<div>{{ data }}</div>',updated() {console.log('更新后DOM:', this.$el.textContent); // 顯示最新data// 若圖表數據依賴this.data,更新后重新繪制if (this.chart) {this.chart.data.datasets[0].data = this.data;this.chart.update();}}
});
五、銷毀階段:組件實例的清理
當組件被銷毀(如v-if="false"
移除組件、路由切換)時,進入銷毀階段,此階段的鉤子函數用于清理資源,避免內存泄漏。
5.1 beforeDestroy
:銷毀前
- 執行時機:組件實例銷毀前調用,此時組件仍處于正常工作狀態。
- 特點:
- 可訪問
data
、methods
、DOM等所有資源。 - 子組件的
beforeDestroy
會在父組件的beforeDestroy
前執行。
- 可訪問
- 適用場景:
- 清理定時器、事件監聽器(避免組件銷毀后仍執行)。
- 取消未完成的請求(避免資源浪費)。
- 解綁自定義事件。
new Vue({el: '#app',data() {return { timer: null };},mounted() {// 啟動定時器this.timer = setInterval(() => {console.log('定時器執行中...');}, 1000);// 綁定事件window.addEventListener('resize', this.handleResize);},beforeDestroy() {// 清理定時器clearInterval(this.timer);// 移除事件監聽window.removeEventListener('resize', this.handleResize);// 取消未完成的請求(假設使用axios)if (this.source) {this.source.cancel('組件銷毀,取消請求');}},methods: {handleResize() { /* 處理窗口大小變化 */ }}
});
5.2 destroyed
:銷毀后
- 執行時機:組件實例銷毀后調用,此時組件的所有資源已被釋放。
- 特點:
- 組件的響應式數據、事件監聽、子組件等均已被銷毀。
- 仍可訪問
$el
,但DOM可能已被移除(取決于銷毀方式)。
- 適用場景:極少使用,可用于最終的資源清理或日志記錄。
new Vue({destroyed() {console.log('組件已銷毀');// 記錄銷毀日志console.log('組件銷毀時間:', new Date().toLocaleString());}
});
六、特殊場景的鉤子函數
除上述核心鉤子外,Vue還提供了針對特殊場景的鉤子函數:
6.1 keep-alive 相關鉤子
keep-alive
用于緩存組件(避免頻繁創建/銷毀),搭配兩個專屬鉤子:
activated
:緩存的組件被激活(顯示)時調用。deactivated
:緩存的組件被停用(隱藏)時調用。
<!-- 緩存組件 -->
<keep-alive><component :is="currentComponent"></component>
</keep-alive><script>
new Vue({data() { return { currentComponent: 'ComponentA' }; },components: {ComponentA: {template: '<div>組件A</div>',activated() {console.log('組件A被激活(從緩存中取出)');// 可在此恢復狀態(如刷新數據)},deactivated() {console.log('組件A被停用(存入緩存)');// 可在此暫停操作(如暫停視頻播放)}}}
});
</script>
6.2 錯誤捕獲鉤子:errorCaptured
用于捕獲子組件拋出的錯誤(Vue 2.5+),返回false
可阻止錯誤向上傳播:
new Vue({errorCaptured(err, vm, info) {console.error('捕獲子組件錯誤:', err, '組件:', vm, '信息:', info);// 返回false阻止錯誤冒泡到控制臺return false;}
});
七、生命周期鉤子的執行順序與實戰示例
7.1 單組件執行順序
new Vue({// 創建階段beforeCreate() { console.log('1. beforeCreate'); },created() { console.log('2. created'); },// 掛載階段beforeMount() { console.log('3. beforeMount'); },mounted() { console.log('4. mounted'); },// 更新階段(觸發條件:修改data)beforeUpdate() { console.log('5. beforeUpdate'); },updated() { console.log('6. updated'); },// 銷毀階段(觸發條件:調用$destroy()或v-if移除)beforeDestroy() { console.log('7. beforeDestroy'); },destroyed() { console.log('8. destroyed'); }
});
執行結果:
初始化時:1→2→3→4
修改數據時:5→6
銷毀時:7→8
7.2 父子組件執行順序
父組件Parent
和子組件Child
的鉤子執行順序:
- 父
beforeCreate
→ 父created
→ 父beforeMount
- 子
beforeCreate
→ 子created
→ 子beforeMount
→ 子mounted
- 父
mounted
- (更新時)父
beforeUpdate
→ 子beforeUpdate
→ 子updated
→ 父updated
- (銷毀時)父
beforeDestroy
→ 子beforeDestroy
→ 子destroyed
→ 父destroyed
結論:父組件等待子組件完成后,才會完成自身對應的階段。
八、常見問題與避坑指南
8.1 避免在updated
中修改數據
// 錯誤示例:導致無限循環
updated() {this.count++; // 修改data觸發更新,再次調用updated,循環往復
}
解決方案:若需根據更新后的數據調整狀態,可使用$nextTick
或條件判斷限制執行次數。
8.2 子組件mounted
晚于父組件mounted
父組件若需等待子組件掛載完成后執行操作(如獲取子組件DOM),需使用$nextTick
或在子組件中通過事件通知父組件:
// 子組件
export default {mounted() {this.$emit('mounted'); // 通知父組件已掛載}
};// 父組件
<child-component @mounted="handleChildMounted"></child-component>
methods: {handleChildMounted() {console.log('子組件已掛載');}
}
8.3 清理資源是重中之重
組件銷毀時若未清理定時器、事件監聽等,會導致內存泄漏(頁面關閉前資源一直占用):
// 錯誤:未清理定時器
mounted() {setInterval(() => { /* 操作 */ }, 1000); // 組件銷毀后仍會執行
}// 正確:在beforeDestroy中清理
beforeDestroy() {clearInterval(this.timer);
}
九、Vue 3中的生命周期變化
Vue 3的Composition API中,生命周期鉤子的使用方式有所調整,但核心邏輯一致:
setup()
:替代beforeCreate
和created
(在兩者之間執行)。onBeforeMount
:對應beforeMount
。onMounted
:對應mounted
。onBeforeUpdate
:對應beforeUpdate
。onUpdated
:對應updated
。onBeforeUnmount
:對應beforeDestroy
。onUnmounted
:對應destroyed
。
// Vue 3 Composition API示例
import { onMounted, onBeforeUnmount } from 'vue';export default {setup() {let timer;onMounted(() => {timer = setInterval(() => { /* 操作 */ }, 1000);});onBeforeUnmount(() => {clearInterval(timer); // 清理資源});}
};
總結
- 精準控制流程:在合適的階段執行合適的操作(如
created
請求數據、mounted
操作DOM)。- 避免常見問題:如更新階段的死循環、銷毀階段的內存泄漏。
- 優化組件性能:合理利用
keep-alive
和緩存鉤子,減少不必要的創建/銷毀。
若這篇內容幫到你,動動手指支持下!關注不迷路,干貨持續輸出!
ヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノ