摘要
Vue3 在保留核心概念的同时,对生命周期系统进行了重要优化和重构。本文将深入探讨 Vue3 生命周期的变化、组合式 API 的使用方式、新旧生命周期对比,通过详细的代码示例、执行流程分析和最佳实践,帮助你全面掌握 Vue3 生命周期的完整知识体系。
一、 Vue3 生命周期概述:为什么需要变化?
1.1 Vue2 生命周期的痛点
在 Vue2 中,生命周期存在一些限制:
- 代码组织分散:相关逻辑分散在不同生命周期钩子中
- 逻辑复用困难:相似的逻辑需要在多个组件中重复编写
- 类型推导有限:TypeScript 支持不够完善
- 代码可读性:大型组件中逻辑关注点分离不清晰
1.2 Vue3 的生命周期改进
Vue3 在生命周期方面的主要改进:
- 更合理的命名 :
beforeDestroy→beforeUnmount,destroyed→unmounted - 组合式 API:更好的逻辑组织和复用
- 更好的 TS 支持:完整的类型推导
- 更灵活的使用:可以在 setup 中直接使用
二、 Vue2 与 Vue3 生命周期完整对比
2.1 生命周期钩子对照表
| Vue2 生命周期 | Vue3 生命周期 | 变化说明 | 执行时机 |
|---|---|---|---|
beforeCreate |
❌ 移除 | 使用 setup() 替代 |
组件初始化前 |
created |
❌ 移除 | 使用 setup() 替代 |
组件创建完成 |
beforeMount |
onBeforeMount |
名称变化,功能相同 | 挂载前 |
mounted |
onMounted |
名称变化,功能相同 | 挂载后 |
beforeUpdate |
onBeforeUpdate |
名称变化,功能相同 | 更新前 |
updated |
onUpdated |
名称变化,功能相同 | 更新后 |
beforeDestroy |
onBeforeUnmount |
名称更准确 | 卸载前 |
destroyed |
onUnmounted |
名称更准确 | 卸载后 |
errorCaptured |
onErrorCaptured |
名称变化,功能相同 | 错误捕获 |
| - | onRenderTracked |
新增 | 渲染依赖跟踪 |
| - | onRenderTriggered |
新增 | 渲染触发跟踪 |
activated |
onActivated |
名称变化,功能相同 | keep-alive 激活 |
deactivated |
onDeactivated |
名称变化,功能相同 | keep-alive 停用 |
2.2 生命周期完整执行流程图
flowchart TD
A[组件初始化] --> B[Setup 执行]
B --> C[onBeforeMount
挂载前] C --> D[DOM 挂载] D --> E[onMounted
挂载完成] E --> F{响应式数据变化?} F -- 是 --> G[onBeforeUpdate
更新前] G --> H[虚拟 DOM 重新渲染] H --> I[onUpdated
更新完成] F -- 否 --> J[组件卸载] J --> K[onBeforeUnmount
卸载前] K --> L[DOM 移除] L --> M[onUnmounted
卸载完成] E --> N[onActivated
keep-alive 激活] N --> O[onDeactivated
keep-alive 停用] style B fill:#e1f5fe style E fill:#e8f5e8 style M fill:#ffebee
挂载前] C --> D[DOM 挂载] D --> E[onMounted
挂载完成] E --> F{响应式数据变化?} F -- 是 --> G[onBeforeUpdate
更新前] G --> H[虚拟 DOM 重新渲染] H --> I[onUpdated
更新完成] F -- 否 --> J[组件卸载] J --> K[onBeforeUnmount
卸载前] K --> L[DOM 移除] L --> M[onUnmounted
卸载完成] E --> N[onActivated
keep-alive 激活] N --> O[onDeactivated
keep-alive 停用] style B fill:#e1f5fe style E fill:#e8f5e8 style M fill:#ffebee
三、 组合式 API 中的生命周期使用
3.1 基本生命周期钩子使用
vue
<template>
<div class="lifecycle-demo">
<h2>Vue3 生命周期演示</h2>
<p>计数: {{ count }}</p>
<p>消息: {{ message }}</p>
<button @click="increment">增加计数</button>
<button @click="updateMessage">更新消息</button>
<button @click="forceUpdate">强制更新</button>
<div v-if="showChild">
<ChildComponent :count="count" />
</div>
<button @click="toggleChild">切换子组件</button>
<div class="log-container">
<h3>生命周期日志:</h3>
<div v-for="(log, index) in logs" :key="index" class="log-item">
{{ log }}
</div>
</div>
</div>
</template>
<script>
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated,
onBeforeUnmount, onUnmounted, onErrorCaptured, nextTick } from 'vue'
import ChildComponent from './ChildComponent.vue'
export default {
name: 'LifecycleDemo',
components: { ChildComponent },
setup() {
const count = ref(0)
const message = ref('Hello Vue3 Lifecycle')
const showChild = ref(true)
const logs = ref([])
// 添加日志函数
const addLog = (hookName, details = '') => {
const timestamp = new Date().toLocaleTimeString()
const log = `[${timestamp}] ${hookName} ${details}`.trim()
logs.value.push(log)
// 保持日志数量不超过20条
if (logs.value.length > 20) {
logs.value.shift()
}
console.log(log)
}
// 1. setup 阶段 - 替代 beforeCreate 和 created
addLog('setup', '组件初始化')
console.log('count 初始值:', count.value)
// 2. onBeforeMount - 挂载前
onBeforeMount(() => {
addLog('onBeforeMount', 'DOM 挂载前')
// 此时无法访问 DOM 元素
console.log('挂载前,无法访问 DOM')
})
// 3. onMounted - 挂载完成
onMounted(() => {
addLog('onMounted', 'DOM 挂载完成')
// 可以访问 DOM 元素
const appElement = document.querySelector('.lifecycle-demo')
console.log('根元素:', appElement)
// 模拟异步操作
setTimeout(() => {
message.value = '异步更新消息'
}, 2000)
})
// 4. onBeforeUpdate - 更新前
onBeforeUpdate(() => {
addLog('onBeforeUpdate', `count: ${count.value}`)
// 此时数据已更新,但 DOM 还未重新渲染
const countElement = document.querySelector('p')
console.log('更新前 count 文本:', countElement?.textContent)
})
// 5. onUpdated - 更新完成
onUpdated(() => {
addLog('onUpdated', `count: ${count.value}`)
// DOM 已更新完成
const countElement = document.querySelector('p')
console.log('更新后 count 文本:', countElement?.textContent)
// 注意:不要在 updated 中同步修改状态,可能导致无限循环
})
// 6. onBeforeUnmount - 卸载前
onBeforeUnmount(() => {
addLog('onBeforeUnmount', '组件卸载前')
// 清理工作,如定时器、事件监听器等
console.log('执行清理操作...')
})
// 7. onUnmounted - 卸载完成
onUnmounted(() => {
addLog('onUnmounted', '组件已卸载')
// 组件已从 DOM 中移除
})
// 8. onErrorCaptured - 错误捕获
onErrorCaptured((error, instance, info) => {
addLog('onErrorCaptured', `错误: ${error.message}`)
console.error('捕获到的错误:', error)
console.log('错误信息:', info)
// 返回 false 阻止错误继续向上传播
return false
})
// 方法定义
const increment = () => {
count.value++
}
const updateMessage = () => {
message.value = `消息已更新 ${Date.now()}`
}
const forceUpdate = async () => {
addLog('forceUpdate', '手动触发更新')
await nextTick()
addLog('nextTick', 'DOM 更新完成')
}
const toggleChild = () => {
showChild.value = !showChild.value
}
return {
count,
message,
showChild,
logs,
increment,
updateMessage,
forceUpdate,
toggleChild
}
},
// 选项式 API 仍然可用(兼容模式)
mounted() {
console.log('选项式 API 的 mounted')
}
}
</script>
<style scoped>
.lifecycle-demo {
padding: 20px;
max-width: 800px;
margin: 0 auto;
font-family: Arial, sans-serif;
}
button {
margin: 5px;
padding: 8px 16px;
background: #42b883;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #369870;
}
.log-container {
margin-top: 30px;
padding: 15px;
background: #f5f5f5;
border-radius: 8px;
max-height: 400px;
overflow-y: auto;
}
.log-item {
padding: 8px 12px;
margin: 4px 0;
background: white;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 12px;
border-left: 4px solid #42b883;
}
</script>
3.2 子组件示例
vue
<template>
<div class="child-component" :style="style">
<h4>子组件</h4>
<p>接收的计数: {{ props.count }}</p>
<p>内部状态: {{ internalState }}</p>
<button @click="changeState">改变状态</button>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, onBeforeMount, watch } from 'vue'
const props = defineProps({
count: {
type: Number,
default: 0
}
})
const internalState = ref('初始状态')
const style = ref({
padding: '15px',
margin: '10px 0',
border: '2px solid #ff6b6b',
borderRadius: '8px',
background: '#fff5f5'
})
// 生命周期钩子
onBeforeMount(() => {
console.log('子组件 - onBeforeMount')
})
onMounted(() => {
console.log('子组件 - onMounted')
// 模拟组件内的异步操作
setTimeout(() => {
internalState.value = '异步更新后的状态'
}, 1000)
})
onUnmounted(() => {
console.log('子组件 - onUnmounted')
})
// 监听 props 变化
watch(() => props.count, (newVal, oldVal) => {
console.log(`子组件检测到 count 变化: ${oldVal} -> ${newVal}`)
// 根据 count 变化更新样式
if (newVal % 2 === 0) {
style.value.background = '#fff5f5'
style.value.borderColor = '#ff6b6b'
} else {
style.value.background = '#f0f9ff'
style.value.borderColor = '#3b82f6'
}
})
const changeState = () => {
internalState.value = `状态改变于 ${new Date().toLocaleTimeString()}`
}
</script>
四、 Vue3 新增的生命周期钩子
4.1 onRenderTracked 和 onRenderTriggered
这两个新增的钩子主要用于调试和性能分析:
vue
<template>
<div class="debug-demo">
<h2>渲染调试钩子演示</h2>
<p>计数: {{ count }}</p>
<p>消息: {{ message }}</p>
<p>计算属性: {{ computedMessage }}</p>
<button @click="increment">增加计数</button>
<button @click="changeMessage">改变消息</button>
<div class="debug-info">
<h3>渲染跟踪信息:</h3>
<div v-for="(info, index) in renderInfo" :key="index" class="info-item">
<span class="type">{{ info.type }}</span>
<span class="target">{{ info.target }}</span>
<span class="time">{{ info.time }}</span>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onRenderTracked, onRenderTriggered } from 'vue'
const count = ref(0)
const message = ref('Hello Debug')
const renderInfo = ref([])
// 计算属性
const computedMessage = computed(() => {
return `${message.value} - 计数: ${count.value}`
})
// 添加调试信息
const addRenderInfo = (type, target) => {
const timestamp = new Date().toLocaleTimeString()
renderInfo.value.unshift({
type,
target: String(target),
time: timestamp
})
if (renderInfo.value.length > 10) {
renderInfo.value.pop()
}
}
// onRenderTracked - 跟踪依赖收集
onRenderTracked((event) => {
addRenderInfo('TRACKED', event.target)
console.log('依赖被跟踪:', event)
})
// onRenderTriggered - 跟踪依赖触发
onRenderTriggered((event) => {
addRenderInfo('TRIGGERED', event.target)
console.log('依赖触发更新:', event)
})
const increment = () => {
count.value++
}
const changeMessage = () => {
message.value = `消息更新 ${Date.now()}`
}
</script>
<style scoped>
.debug-demo {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.debug-info {
margin-top: 20px;
padding: 15px;
background: #2c3e50;
color: white;
border-radius: 8px;
}
.info-item {
display: flex;
justify-content: space-between;
padding: 8px;
margin: 4px 0;
background: #34495e;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 12px;
}
.type {
color: #42b883;
font-weight: bold;
}
.target {
color: #e0e0e0;
flex: 1;
margin: 0 10px;
}
.time {
color: #bdc3c7;
}
</style>
4.2 服务端渲染相关生命周期
vue
<template>
<div class="ssr-demo">
<h2>服务端渲染生命周期</h2>
<p>当前环境: {{ isServer ? '服务端' : '客户端' }}</p>
<p>数据: {{ serverData }}</p>
</div>
</template>
<script setup>
import { ref, onServerPrefetch, onMounted } from 'vue'
const isServer = ref(typeof window === 'undefined')
const serverData = ref(null)
// 服务端数据预取
onServerPrefetch(async () => {
console.log('服务端数据预取...')
// 模拟服务端 API 调用
serverData.value = await fetchServerData()
})
// 客户端激活
onMounted(() => {
console.log('客户端激活完成')
// 如果服务端没有预取数据,在客户端获取
if (!serverData.value) {
fetchServerData().then(data => {
serverData.value = data
})
}
})
// 模拟数据获取
const fetchServerData = async () => {
return new Promise(resolve => {
setTimeout(() => {
resolve(`服务器数据 - ${new Date().toISOString()}`)
}, 100)
})
}
</script>
五、 Keep-alive 相关生命周期
5.1 onActivated 和 onDeactivated
vue
<template>
<div class="keep-alive-demo">
<h2>Keep-alive 生命周期演示</h2>
<div class="controls">
<button
v-for="tab in tabs"
:key="tab.id"
@click="currentTab = tab.id"
:class="{ active: currentTab === tab.id }"
>
{{ tab.name }}
</button>
</div>
<keep-alive>
<component :is="currentComponent" :key="currentTab" />
</keep-alive>
</div>
</template>
<script setup>
import { ref, computed, markRaw } from 'vue'
import Tab1 from './components/Tab1.vue'
import Tab2 from './components/Tab2.vue'
import Tab3 from './components/Tab3.vue'
const currentTab = ref('tab1')
const tabs = [
{ id: 'tab1', name: '选项卡1', component: markRaw(Tab1) },
{ id: 'tab2', name: '选项卡2', component: markRaw(Tab2) },
{ id: 'tab3', name: '选项卡3', component: markRaw(Tab3) }
]
const currentComponent = computed(() => {
return tabs.find(tab => tab.id === currentTab.value)?.component
})
</script>
<style scoped>
.keep-alive-demo {
padding: 20px;
}
.controls {
margin-bottom: 20px;
}
.controls button {
margin: 0 5px;
padding: 10px 20px;
background: #ecf0f1;
border: none;
border-radius: 4px;
cursor: pointer;
}
.controls button.active {
background: #42b883;
color: white;
}
</style>
Tab1.vue - 缓存组件示例
vue
<template>
<div class="tab-content">
<h3>选项卡 1 (被缓存)</h3>
<p>计数: {{ count }}</p>
<p>最后激活时间: {{ lastActivated }}</p>
<button @click="increment">增加计数</button>
<button @click="fetchData">模拟数据请求</button>
<div v-if="data" class="data-display">
<h4>数据内容:</h4>
<p>{{ data }}</p>
</div>
</div>
</template>
<script setup>
import { ref, onActivated, onDeactivated, onMounted, onUnmounted } from 'vue'
const count = ref(0)
const lastActivated = ref(null)
const data = ref(null)
// 组件激活时调用
onActivated(() => {
lastActivated.value = new Date().toLocaleTimeString()
console.log('Tab1 被激活')
// 可以在这里重新启动定时器、重新订阅事件等
startTimer()
})
// 组件停用时调用
onDeactivated(() => {
console.log('Tab1 被停用')
// 可以在这里暂停定时器、取消订阅等
stopTimer()
})
// 正常生命周期
onMounted(() => {
console.log('Tab1 mounted')
startTimer()
})
onUnmounted(() => {
console.log('Tab1 unmounted')
stopTimer()
})
// 定时器示例
let timer = null
const startTimer = () => {
if (!timer) {
timer = setInterval(() => {
console.log('Tab1 定时器运行中...')
}, 3000)
}
}
const stopTimer = () => {
if (timer) {
clearInterval(timer)
timer = null
console.log('Tab1 定时器已停止')
}
}
const increment = () => {
count.value++
}
const fetchData = async () => {
// 模拟异步数据请求
data.value = '加载中...'
setTimeout(() => {
data.value = `数据加载完成: ${new Date().toLocaleTimeString()}`
}, 1000)
}
</script>
<style scoped>
.tab-content {
padding: 20px;
border: 2px solid #42b883;
border-radius: 8px;
background: #f8fff8;
}
.data-display {
margin-top: 15px;
padding: 10px;
background: white;
border-radius: 4px;
border: 1px solid #e0e0e0;
}
</style>
六、 生命周期最佳实践和常见陷阱
6.1 生命周期使用最佳实践
vue
<template>
<div class="best-practice-demo">
<h2>生命周期最佳实践</h2>
<div class="practice-section">
<h3>1. 异步操作处理</h3>
<button @click="startAsyncOperation">开始异步操作</button>
<button @click="cancelAsyncOperation">取消异步操作</button>
<p>操作状态: {{ operationStatus }}</p>
</div>
<div class="practice-section">
<h3>2. 事件监听器清理</h3>
<button @click="toggleEventListener">切换事件监听</button>
<p>监听状态: {{ isListening ? '开启' : '关闭' }}</p>
</div>
<div class="practice-section">
<h3>3. 定时器管理</h3>
<button @click="toggleTimer">切换定时器</button>
<p>定时器计数: {{ timerCount }}</p>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, onBeforeUnmount } from 'vue'
// 1. 异步操作处理
const operationStatus = ref('空闲')
let asyncOperationController = null
const startAsyncOperation = async () => {
// 如果已有操作在进行,先取消
if (asyncOperationController) {
asyncOperationController.abort()
}
asyncOperationController = new AbortController()
operationStatus.value = '进行中'
try {
// 模拟长时间异步操作
await new Promise((resolve, reject) => {
const timeout = setTimeout(resolve, 3000)
// 监听取消信号
asyncOperationController.signal.addEventListener('abort', () => {
clearTimeout(timeout)
reject(new Error('操作被取消'))
})
})
if (!asyncOperationController.signal.aborted) {
operationStatus.value = '完成'
}
} catch (error) {
if (error.message !== '操作被取消') {
operationStatus.value = '失败'
console.error('操作失败:', error)
} else {
operationStatus.value = '已取消'
}
} finally {
asyncOperationController = null
}
}
const cancelAsyncOperation = () => {
if (asyncOperationController) {
asyncOperationController.abort()
}
}
// 2. 事件监听器清理
const isListening = ref(false)
let resizeListener = null
const toggleEventListener = () => {
if (isListening.value) {
removeEventListener()
} else {
addEventListener()
}
}
const addEventListener = () => {
if (!resizeListener) {
resizeListener = () => {
console.log('窗口大小改变:', window.innerWidth, 'x', window.innerHeight)
}
window.addEventListener('resize', resizeListener)
isListening.value = true
console.log('事件监听器已添加')
}
}
const removeEventListener = () => {
if (resizeListener) {
window.removeEventListener('resize', resizeListener)
resizeListener = null
isListening.value = false
console.log('事件监听器已移除')
}
}
// 3. 定时器管理
const timerCount = ref(0)
let timer = null
const toggleTimer = () => {
if (timer) {
stopTimer()
} else {
startTimer()
}
}
const startTimer = () => {
if (!timer) {
timer = setInterval(() => {
timerCount.value++
console.log('定时器计数:', timerCount.value)
}, 1000)
console.log('定时器已启动')
}
}
const stopTimer = () => {
if (timer) {
clearInterval(timer)
timer = null
console.log('定时器已停止')
}
}
// 组件挂载时初始化
onMounted(() => {
console.log('组件挂载完成,执行初始化操作')
addEventListener() // 默认开启事件监听
})
// 组件卸载前清理
onBeforeUnmount(() => {
console.log('组件即将卸载,执行清理操作')
cancelAsyncOperation() // 取消进行中的异步操作
removeEventListener() // 移除事件监听
stopTimer() // 停止定时器
})
// 也可以使用 onUnmounted
onUnmounted(() => {
console.log('组件已卸载')
})
</script>
<style scoped>
.best-practice-demo {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.practice-section {
margin: 30px 0;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background: #fafafa;
}
.practice-section h3 {
color: #2c3e50;
margin-top: 0;
}
button {
margin: 5px;
padding: 8px 16px;
background: #42b883;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #369870;
}
</style>
6.2 常见陷阱和解决方案
vue
<template>
<div class="pitfalls-demo">
<h2>生命周期常见陷阱</h2>
<div class="pitfall-item">
<h3>陷阱1: 在 updated 中修改状态导致无限循环</h3>
<button @click="count++">增加计数 ({{ count }})</button>
<p>更新次数: {{ updateCount }}</p>
</div>
<div class="pitfall-item">
<h3>陷阱2: 忘记清理资源导致内存泄漏</h3>
<button @click="showLeakyComponent = !showLeakyComponent">
{{ showLeakyComponent ? '隐藏' : '显示' }} 可能泄漏的组件
</button>
<LeakyComponent v-if="showLeakyComponent" />
</div>
<div class="pitfall-item">
<h3>陷阱3: 错误的异步操作时机</h3>
<button @click="fetchDataWithTiming">有问题的数据获取</button>
<button @click="fetchDataCorrectly">正确的数据获取</button>
<p>数据: {{ fetchedData }}</p>
</div>
</div>
</template>
<script setup>
import { ref, onUpdated, onMounted, onUnmounted } from 'vue'
import LeakyComponent from './LeakyComponent.vue'
// 陷阱1: 无限循环
const count = ref(0)
const updateCount = ref(0)
onUpdated(() => {
updateCount.value++
console.log(`第 ${updateCount.value} 次更新`)
// 错误做法: 在 updated 中同步修改响应式数据
// count.value++ // 这会导致无限循环!
// 正确做法: 使用条件判断或异步操作
if (count.value < 5) {
// 如果需要,使用 nextTick 或 setTimeout
// setTimeout(() => { count.value++ }, 0)
}
})
// 陷阱2: 内存泄漏
const showLeakyComponent = ref(false)
// 陷阱3: 异步操作时机
const fetchedData = ref('')
// 错误做法: 在可能卸载的组件中不处理异步操作
const fetchDataWithTiming = () => {
setTimeout(() => {
// 如果组件在数据返回前已卸载,这里会报错
fetchedData.value = '延迟数据'
}, 2000)
}
// 正确做法: 使用标志位或 AbortController
let isMounted = true
const fetchDataCorrectly = () => {
fetchedData.value = '加载中...'
setTimeout(() => {
// 检查组件是否仍然挂载
if (isMounted) {
fetchedData.value = '安全的数据加载'
}
}, 2000)
}
onMounted(() => {
isMounted = true
})
onUnmounted(() => {
isMounted = false
})
</script>
<style scoped>
.pitfalls-demo {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.pitfall-item {
margin: 25px 0;
padding: 20px;
border: 2px solid #ff6b6b;
border-radius: 8px;
background: #fff5f5;
}
.pitfall-item h3 {
color: #e53e3e;
margin-top: 0;
}
button {
margin: 5px;
padding: 8px 16px;
background: #e53e3e;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #c53030;
}
</style>
七、 总结
7.1 Vue3 生命周期核心变化总结
- 更合理的命名 :
beforeUnmount和unmounted更准确描述组件卸载 - 组合式 API :在
setup()中使用生命周期函数,更好的逻辑组织 - 新增调试钩子 :
onRenderTracked和onRenderTriggered用于开发调试 - 更好的 TypeScript 支持:完整的类型定义和智能提示
7.2 生命周期使用指南
| 场景 | 推荐的生命周期 | 说明 |
|---|---|---|
| 数据初始化 | setup() |
替代 beforeCreate 和 created |
| DOM 操作 | onMounted |
确保 DOM 已挂载 |
| 事件监听 | onMounted 添加,onUnmounted 移除 |
防止内存泄漏 |
| 定时器 | onMounted 启动,onUnmounted 清理 |
防止内存泄漏 |
| 异步操作 | onMounted + 清理函数 |
处理组件卸载时的操作取消 |
| 响应式数据更新 | onUpdated(谨慎使用) |
避免无限循环 |
| 性能调试 | onRenderTracked / onRenderTriggered |
开发阶段使用 |
7.3 迁移建议
- Vue2 → Vue3:逐步替换生命周期函数名称
- 新项目:优先使用组合式 API 和新的生命周期函数
- 大型项目:可以混合使用选项式和组合式 API
- TypeScript 项目:充分利用完整的类型支持
Vue3 的生命周期系统在保持易用性的同时,提供了更强大的功能和更好的开发体验。通过合理使用生命周期钩子,可以构建出更健壮、更易维护的 Vue 应用。