生命周期
约 1621 字大约 5 分钟
2026-01-29
重要
Nuxt 生命周期由三层组成:Vue 组件生命周期、Nuxt 应用生命周期(hooks)、Nitro 服务端生命周期
最佳实践
- 访问浏览器 API(
window、document、localStorage)只放在onMounted或.client.ts - 避免在
app:created做重任务,放到page:finish或懒加载逻辑 - 需要清理的副作用在
onUnmounted中释放(定时器、事件监听) - 页面级逻辑用
definePageMeta,应用级逻辑用插件defineNuxtPlugin - 使用
useState存储全局状态,避免跨请求共享导致 SSR 污染 app:mounted只在客户端触发,服务端不应依赖它
核心概念
Nuxt 的生命周期可分为:组件生命周期、应用生命周期与服务端生命周期
组件生命周期(Vue)
组件生命周期用于管理组件自身状态与副作用,具体可参考 Vue 组件生命周期钩子
<script setup lang="ts">
import { onMounted, onUnmounted, onServerPrefetch, ref } from 'vue'
const count = ref(0)
let timer: ReturnType<typeof setInterval> | null = null
// 服务端预取数据:只在 SSR 时执行
onServerPrefetch(async () => {
// 例如:服务端提前拉取数据
})
// 仅客户端执行:适合 DOM 操作与浏览器 API
onMounted(() => {
timer = setInterval(() => {
count.value += 1
}, 1000)
})
// 组件卸载时清理副作用
onUnmounted(() => {
if (timer) clearInterval(timer)
})
</script>
<template>
<p>计数器:{{ count }}</p>
</template>应用生命周期(Runtime Hooks)
应用生命周期用于全局逻辑,例如埋点、性能统计、统一错误处理等,具体可参考 Nuxt 应用生命周期钩子
export default defineNuxtPlugin((nuxtApp) => {
// 应用创建:该示例为 .client 插件,仅在客户端执行
nuxtApp.hook('app:created', () => {
console.log('[app] created')
})
// 应用挂载:仅客户端执行
nuxtApp.hook('app:mounted', () => {
console.log('[app] mounted')
})
// 页面开始切换
nuxtApp.hook('page:start', () => {
console.log('[page] start')
})
// 页面切换完成
nuxtApp.hook('page:finish', () => {
console.log('[page] finish')
})
// 全局错误
nuxtApp.hook('app:error', (error) => {
console.error('[app] error:', error)
})
})提示
在组件中也可以用 useRuntimeHook 监听运行时 hooks,更适合页面内局部逻辑。
<script setup lang="ts">
useRuntimeHook('page:finish', () => {
console.log('页面已渲染完成')
})
</script>Nitro 服务端生命周期(Server Hooks)
服务端 hooks 用于请求与响应处理,如注入 HTML、统计访问等,具体可以参考 Nitro 服务端生命周期钩子
export default defineNitroPlugin((nitroApp) => {
// 请求进入
nitroApp.hooks.hook('request', (event) => {
console.log('[server] request:', event.path)
})
// 修改 HTML 输出
nitroApp.hooks.hook('render:html', (html) => {
html.head.push('<meta name="app" content="short-note">')
})
// 响应结束
nitroApp.hooks.hook('afterResponse', (event) => {
console.log('[server] done:', event.path)
})
})构建期 Hooks(nuxt.config.ts)
构建期 hooks 用于扩展构建流程与目录扫描等行为,通常在 nuxt.config.ts 中进行配置
export default defineNuxtConfig({
hooks: {
'build:before': () => {
console.log('Build start')
},
'components:dirs': (dirs) => {
// 扩展组件目录
dirs.push({ path: '~/extra-components' })
},
},
})生命周期执行顺序
笔记
以下为“典型流程”,实际顺序会因重定向、错误中断、缓存命中、组件复用等情况发生变化。
SSR 首次请求
即:服务端,从请求到返回 HTML
典型流程
- 首先执行
server/plugins(每个服务进程启动时一次,冷启动会重新执行),用于注册 Nitro hooks 与运行时配置 - 接着进入
server/middleware(每个请求都会执行),完成鉴权、重写、日志等处理 - 然后创建 Vue/Nuxt 实例并执行
app/plugins,注入全局能力 - 随后触发
app:created,表示 Nuxt app 初始化完成 - 接着执行路由
validate与路由中间件(app/middleware),完成参数校验、准入与跳转逻辑 - 之后运行 服务端
setup,并同步触发onServerPrefetch、useFetch/useAsyncData获取首屏数据 - 渲染完成触发
app:rendered,表示 HTML 已准备好 - 接着触发
render:html,进行最终 HTML 处理与注入 - 最后进入
render:response / beforeResponse / afterResponse并发送响应,结束本次请求
- 首先执行
客户端首次渲染
即:Hydration
典型流程
- 浏览器接收 HTML,创建应用实例并执行
app/plugins - 接着触发
app:created - 然后执行路由
validate与路由中间件 - 之后进入 Hydration 流程:创建组件实例并执行
setup - 然后触发
app:beforeMount(应用即将挂载) - 接着执行挂载与 DOM 对齐(Hydration 完成),触发
app:mounted - 最后
onMounted执行
重要
setup会在服务端与客户端都执行,必须保证无浏览器依赖- 浏览器接收 HTML,创建应用实例并执行
客户端路由切换
常见触发顺序
- 导航触发
- 首先
page:start(Suspense pending) - 接着
page:loading:start(新页面setup正在执行) - 然后
page:finish(页面与数据准备完成) - 最后
page:loading:end
其中
page:loading:start与page:loading:end是专门用于页面加载指示器的客户端事件。仅在客户端路由导航(如使用<NuxtLink>、navigateTo或router.push)时触发重要
组件生命周期主要嵌套在
page:start ~ page:finish之间,且旧页面先卸载、新页面再挂载
示例:页面性能统计
准备目录结构
app
plugins
perf.client.ts
composables
usePagePerf.ts
pages
index.vue
插件监听页面切换生命周期
app/plugins/perf.client.tsexport default defineNuxtPlugin((nuxtApp) => { // 用 useState 避免 SSR 跨请求污染 const perf = useState('page-perf', () => ({ start: 0, duration: 0 })) nuxtApp.hook('page:start', () => { perf.value.start = performance.now() }) nuxtApp.hook('page:finish', () => { perf.value.duration = Math.round(performance.now() - perf.value.start) }) })抽成 composable 供页面读取
app/composables/usePagePerf.tsexport function usePagePerf() { return useState('page-perf', () => ({ start: 0, duration: 0 })) }页面展示耗时结果
app/pages/index.vue<script setup lang="ts"> const perf = usePagePerf() </script> <template> <section> <h1>首页</h1> <p>最近一次页面切换耗时:{{ perf.duration }} ms</p> </section> </template>
