生命周期钩子是Vue组件从创建到销毁过程中的特定时刻,允许我们在这些时刻执行代码。
创建阶段:
setup() / beforeCreate
↓
created
↓
挂载阶段:
beforeMount
↓
mounted (DOM已创建)
↓
更新阶段:
beforeUpdate
↓
updated
↓
卸载阶段:
beforeUnmount
↓
unmounted
<script setup>
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue'
// setup函数本身相当于beforeCreate和created
onBeforeMount(() => {
console.log('组件挂载前')
})
onMounted(() => {
console.log('组件已挂载,DOM已创建')
// 适合:访问DOM、发起API请求、设置定时器
})
onBeforeUpdate(() => {
console.log('数据更新前,DOM还未更新')
})
onUpdated(() => {
console.log('数据和DOM都已更新')
// 注意:避免在这里修改状态,可能导致无限循环
})
onBeforeUnmount(() => {
console.log('组件卸载前')
// 适合:清理定时器、取消订阅
})
onUnmounted(() => {
console.log('组件已卸载')
})
</script>
<script>
export default {
beforeCreate() {
console.log('实例初始化后,数据观测前')
},
created() {
console.log('实例创建完成,数据观测完成')
// 适合:初始化数据、发起API请求
},
beforeMount() {
console.log('挂载开始前')
},
mounted() {
console.log('挂载完成,DOM已创建')
// 适合:访问DOM、第三方库初始化
},
beforeUpdate() {
console.log('数据更新前')
},
updated() {
console.log('数据更新后')
},
beforeUnmount() {
console.log('卸载前')
},
unmounted() {
console.log('卸载完成')
}
}
</script>
<template>
<div ref="chartRef"></div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const chartRef = ref(null)
onMounted(() => {
// 1. 访问DOM元素
console.log(chartRef.value)
// 2. 初始化第三方库
// initChart(chartRef.value)
// 3. 发起API请求
fetchData()
// 4. 添加事件监听
window.addEventListener('resize', handleResize)
})
function fetchData() {
console.log('获取数据')
}
function handleResize() {
console.log('窗口大小改变')
}
</script>
<script setup>
import { onMounted, onUnmounted } from 'vue'
let timer = null
onMounted(() => {
// 设置定时器
timer = setInterval(() => {
console.log('定时执行')
}, 1000)
// 添加事件监听
window.addEventListener('scroll', handleScroll)
})
onUnmounted(() => {
// 清理定时器
if (timer) {
clearInterval(timer)
}
// 移除事件监听
window.removeEventListener('scroll', handleScroll)
})
function handleScroll() {
console.log('滚动')
}
</script>
<template>
<div>
<p ref="textRef">{{ count }}</p>
<button @click="count++">增加</button>
</div>
</template>
<script setup>
import { ref, onBeforeUpdate, onUpdated } from 'vue'
const count = ref(0)
const textRef = ref(null)
onBeforeUpdate(() => {
console.log('更新前的DOM:', textRef.value?.textContent)
})
onUpdated(() => {
console.log('更新后的DOM:', textRef.value?.textContent)
})
</script>
<script setup>
import { onActivated, onDeactivated } from 'vue'
// 组件被激活时调用
onActivated(() => {
console.log('组件激活')
// 适合:恢复数据、重新订阅
})
// 组件被停用时调用
onDeactivated(() => {
console.log('组件停用')
// 适合:暂停操作、取消订阅
})
</script>
<script setup>
import { onErrorCaptured } from 'vue'
// 捕获子组件错误
onErrorCaptured((err, instance, info) => {
console.error('捕获到错误:', err)
console.log('错误组件:', instance)
console.log('错误信息:', info)
// 返回false阻止错误继续传播
return false
})
</script>
<template>
<div>
<h2>计时器: {{ time }}秒</h2>
<button @click="start">开始</button>
<button @click="pause">暂停</button>
<button @click="reset">重置</button>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const time = ref(0)
let timer = null
let isRunning = false
function start() {
if (!isRunning) {
isRunning = true
timer = setInterval(() => {
time.value++
}, 1000)
}
}
function pause() {
isRunning = false
if (timer) {
clearInterval(timer)
timer = null
}
}
function reset() {
pause()
time.value = 0
}
onMounted(() => {
console.log('计时器组件已挂载')
})
onUnmounted(() => {
console.log('计时器组件已卸载')
pause() // 清理定时器
})
</script>
<template>
<div>
<div v-if="loading">加载中...</div>
<div v-else-if="error">错误: {{ error }}</div>
<div v-else>
<ul>
<li v-for="user in users" :key="user.id">
{{ user.name }}
</li>
</ul>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const users = ref([])
const loading = ref(false)
const error = ref(null)
async function fetchUsers() {
loading.value = true
error.value = null
try {
const response = await fetch('/api/users')
users.value = await response.json()
} catch (e) {
error.value = e.message
} finally {
loading.value = false
}
}
onMounted(() => {
fetchUsers()
})
</script>
<template>
<div>
<p>窗口宽度: {{ width }}px</p>
<p>窗口高度: {{ height }}px</p>
<p>设备类型: {{ deviceType }}</p>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
const width = ref(window.innerWidth)
const height = ref(window.innerHeight)
const deviceType = computed(() => {
if (width.value < 768) return '移动设备'
if (width.value < 1024) return '平板'
return '桌面'
})
function handleResize() {
width.value = window.innerWidth
height.value = window.innerHeight
}
onMounted(() => {
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
})
</script>
<script setup>
import { ref, onMounted, onUpdated } from 'vue'
const count = ref(0)
// 错误:在onUpdated中修改状态
onUpdated(() => {
count.value++ // 导致无限循环!
})
// 正确:使用watch
import { watch } from 'vue'
watch(count, (newValue) => {
console.log('count变化:', newValue)
})
// 错误:忘记清理
onMounted(() => {
setInterval(() => {
console.log('定时器')
}, 1000)
// 组件卸载后定时器仍在运行!
})
// 正确:在onUnmounted中清理
let timer = null
onMounted(() => {
timer = setInterval(() => {
console.log('定时器')
}, 1000)
})
onUnmounted(() => {
if (timer) clearInterval(timer)
})
</script>