Lifecycle Hooks,感覺蠻有趣~生命週期有哪些狀態?
照理說應該會看到一個流程圖的樣子~
對於生命週期都會覺得比較常用通常是某幾個階段~
我只想要Destroy這個生命週期~www
每個 Vue 組件實例在創建時會經歷一系列的初始化步驟例。例如,它需要設置數據觀測
、編譯模板
、將實例掛載到 DOM 上
,並在數據變更時更新 DOM
。在這些過程中,它會執行稱為生命週期鉤子函數,這些鉤子允許用戶在特定階段插入自己的代碼。
例如,onMounted
鉤子可以在組件完成初始渲染並創建 DOM 節點後運行代碼:
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
console.log(`組件現在已經掛載。`)
})
</script>
還有其他鉤子會在實例的不同生命週期階段被調用,其中最常用的是 onMounted
、onUpdated
和 onUnmounted
。
調用 onMounted
時,Vue 會自動將註冊的回調函數與當前活躍的組件實例關聯起來。這要求這些鉤子必須在組件設置期間同步
註冊。例如,不要這樣做:
setTimeout(() => {
onMounted(() => {
// 這樣做不會奏效。
})
}, 100)
注意,這並不意味著調用必須放置在 setup()
或 <script setup>
內部。onMounted()
可以在外部函數中調用,只要調用堆棧是同步的,並且源自 setup()
內部即可。
在 Vue 中,onMounted
鉤子需要在組件的設置階段同步註冊。這是因為 Vue 需要在組件實例的生命週期內部確保所有的鉤子都已正確註冊。如果你在非同步操作中(例如 setTimeout
)調用 onMounted
,這樣的註冊可能會在組件的生命週期階段之外進行,因此不會如預期地工作。
setTimeout
是一個非同步操作,它會在指定的時間(這裡是 100 毫秒)後執行其回調函數。onMounted
鉤子的註冊會被延遲到 setTimeout
的回調中。由於 setTimeout
是非同步的,這可能會在 Vue 的組件實例已經完成設置之後執行,這時組件的生命週期已經不再適合進行這些鉤子的註冊。onMounted
的回調函數可能無法正確地與組件實例關聯,從而導致該鉤子不會如預期執行。要確保 onMounted
鉤子能正常工作,它應該在組件的設置過程中同步註冊,而不是在非同步操作中。這樣 Vue 可以在適當時間點觸發這些鉤子,保證能正確地執行。
下圖展示了 Vue 組件實例的生命周期。在你剛開始學習時,可能不需要完全理解其中的每個細節,但隨著你學習和構建更多的項目,它將成為一個有用的參考工具。
onBeforeMount
: 當組件即將被掛載到 DOM 上時調用。此時,虛擬 DOM 已經渲染完畢,但真實的 DOM 還沒有更新。onMounted
: 組件已經掛載到 DOM 上時調用。此時,DOM 已經可用,可以進行 DOM 操作。onBeforeUpdate
: 當組件的響應式數據發生變化並即將更新 DOM 之前調用。可以在這裡查看更新前的狀態。onUpdated
: 組件的 DOM 更新完成後調用。此時可以獲取到最新的 DOM 狀態。onBeforeUnmount
: 組件即將從 DOM 上移除時調用。可以在這裡進行一些清理工作。onUnmounted
: 組件已經從 DOM 上移除後調用。此時組件的所有響應式數據、監聽器等都已經被銷毀。除了上面提到的常見鉤子外,還有一些其他鉤子在特定場景下非常有用:
onActivated
: 當組件從 <keep-alive>
緩存中激活時調用。onDeactivated
: 當組件被 <keep-alive>
緩存時調用。onErrorCaptured
: 當捕獲到組件內部的錯誤時調用,用於錯誤邊界處理。<template>
<div>
<p>Component Lifecycle Hooks Example</p>
</div>
</template>
<script setup>
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onActivated, onDeactivated, onErrorCaptured } from 'vue'
onBeforeMount(() => {
console.log('beforeMount - 組件即將掛載')
})
onMounted(() => {
console.log('mounted - 組件已掛載')
})
onBeforeUpdate(() => {
console.log('beforeUpdate - 組件即將更新')
})
onUpdated(() => {
console.log('updated - 組件已更新')
})
onBeforeUnmount(() => {
console.log('beforeUnmount - 組件即將銷毀')
})
onUnmounted(() => {
console.log('unmounted - 組件已銷毀')
})
onActivated(() => {
console.log('activated - 組件已激活')
})
onDeactivated(() => {
console.log('deactivated - 組件已停用')
})
onErrorCaptured((err, instance, info) => {
console.error('errorCaptured - 捕獲到錯誤:', err, info)
})
</script>
beforeMount - 組件即將掛載
mounted - 組件已掛載
beforeUpdate - 組件即將更新
updated - 組件已更新
activated - 組件已激活
deactivated - 組件已停用
beforeUnmount - 組件即將銷毀
unmounted - 組件已銷毀
errorCaptured - 捕獲到錯誤: ...
請注意,如果你的組件沒有使用 <keep-alive>
,則 activated
和 deactivated
鉤子不會觸發。如果你的組件或其子組件沒有拋出錯誤,則 errorCaptured
鉤子也不會觸發。
若要了解所有生命周期鉤子及其相應的使用案例,請查閱 Vue 的生命周期鉤子 API 參考文件。這些鉤子允許你在 Vue 組件實例的不同階段執行自定義代碼,使你能夠更靈活地控制組件的行為。
<keep-alive>
是 Vue 提供的一個內建組件,用於包裹動態組件,從而在其激活和停用之間保留組件的狀態或避免重新渲染。這在需要在不同視圖之間切換時非常有用,例如導航標籤或多頁應用。
activated
鉤子:<keep-alive>
包裹且激活時(即重新顯示)會觸發。deactivated
鉤子:<keep-alive>
包裹且停用時(即隱藏)會觸發。假設有一個包含兩個子組件的 Vue 應用,每個子組件會在不同的選項卡中顯示。我們使用 <keep-alive>
來包裹這些子組件,這樣在切換選項卡時不會重新創建和銷毀組件,而是保留其狀態。
<template>
<div>
<button @click="currentView = 'view1'">View 1</button>
<button @click="currentView = 'view2'">View 2</button>
<keep-alive>
<component :is="currentView"></component>
</keep-alive>
</div>
</template>
<script setup>
import { ref } from 'vue'
import View1 from './View1.vue'
import View2 from './View2.vue'
const currentView = ref('view1')
</script>
有兩個按鈕,分別用來切換當前顯示的視圖。
@click="currentView = 'view1'"
:點擊這個按鈕會將 currentView
設置為 'view1'
。@click="currentView = 'view2'"
:點擊這個按鈕會將 currentView
設置為 'view2'
。<keep-alive>
是一個 Vue 的內建組件,用來包裹動態組件。它會在組件激活和停用之間保持其狀態,不會每次都重新創建和銷毀組件。<component :is="currentView"></component>
:<component>
是一個動態組件,可以根據 currentView
的值來渲染不同的組件。currentView
的值決定了哪個組件會被渲染。:is
是一個綁定屬性,用來指定要渲染的組件名稱。這個屬性可以接受一個字符串或一個組件對象。View1.vue
中的 activated
和 deactivated
<template>
<div>
<p>This is View 1</p>
</div>
</template>
<script setup>
import { onActivated, onDeactivated } from 'vue'
onActivated(() => {
console.log('View 1 is activated')
})
onDeactivated(() => {
console.log('View 1 is deactivated')
})
</script>
View2.vue
<template>
<div>
<p>This is View 2</p>
</div>
</template>
<script setup>
import { onActivated, onDeactivated } from 'vue'
onActivated(() => {
console.log('View 2 is activated')
})
onDeactivated(() => {
console.log('View 2 is deactivated')
})
</script>
當你在選項卡之間切換時,你會在控制台看到類似以下的輸出:
View 1 is activated
View 1 is deactivated
View 2 is activated
View 2 is deactivated
生命週期這玩意常常搞得我一個頭兩個大~
每個鉤子放log,看看每個週期的先後順序跟邏輯
然後就是觀察自動同步的時候是哪個週期?
<template>
<div>
<p>Component Lifecycle Hooks Logging Example</p>
<button @click="updateMessage">Update Message</button>
<p>{{ message }}</p>
</div>
</template>
<script setup>
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'
const message = ref('Hello, Vue!')
// 生命週期鉤子放置 log
onBeforeMount(() => {
console.log('beforeMount - 組件即將掛載')
})
onMounted(() => {
console.log('mounted - 組件已掛載')
})
onBeforeUpdate(() => {
console.log('beforeUpdate - 組件即將更新')
})
onUpdated(() => {
console.log('updated - 組件已更新')
})
onBeforeUnmount(() => {
console.log('beforeUnmount - 組件即將銷毀')
})
onUnmounted(() => {
console.log('unmounted - 組件已銷毀')
})
// 更新消息的方法
const updateMessage = () => {
message.value = 'Message updated!'
}
</script>
在上述示例中,當點擊按鈕更新消息時,可以看到 beforeUpdate
和 updated
鉤子的觸發順序。這表明在數據變化後,Vue 開始自動同步 DOM,在此期間 beforeUpdate
鉤子被觸發,DOM 更新完成後 updated
鉤子被觸發。