生命周期钩子
Nuxt 提供三层钩子系统,各层在不同时机执行:
层级 执行时机 典型用途 App Hooks 运行时(客户端 + 服务端) 页面加载动画、错误上报、路由拦截 Nuxt Hooks 构建时 添加页面/组件、修改配置、代码生成 Nitro Hooks 服务端运行时 请求日志、响应修改、错误处理
为什么需要三层?
App Hooks 处理用户交互和页面生命周期,Nuxt Hooks 处理构建时的代码生成和配置修改,Nitro Hooks 处理服务端请求生命周期。职责分离,避免混乱。
App Hooks
在客户端和服务端运行,用于应用级事件。最常在插件中使用。
在插件中使用
ts
export default defineNuxtPlugin((nuxtApp) => {
// 页面加载
nuxtApp.hook('page:start', () => {
console.log('页面开始加载')
})
nuxtApp.hook('page:finish', () => {
console.log('页面加载完成')
})
// 应用挂载(仅客户端)
nuxtApp.hook('app:mounted', () => {
console.log('应用已挂载')
})
// 错误
nuxtApp.hook('vue:error', (error, instance, info) => {
console.error('Vue 错误:', error)
})
nuxtApp.hook('app:error', (error) => {
console.error('应用错误:', error)
})
})实用示例
页面加载进度条
ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('page:start', () => {
NProgress.start() // 显示加载条
})
nuxtApp.hook('page:finish', () => {
NProgress.done() // 隐藏加载条
})
})全局错误上报到 Sentry
ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('vue:error', (error) => {
Sentry.captureException(error)
})
nuxtApp.hook('app:error', (error) => {
Sentry.captureException(error)
})
})自定义链接预取
ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('link:prefetch', ({ to }) => {
// 只预取特定路径
if (to.startsWith('/blog')) return true
return false // 其他路径不预取
})
})常用 App Hooks
| 钩子 | 参数 | 说明 | 典型用途 |
|---|---|---|---|
app:created | vueApp | Vue 应用创建 | 初始化第三方库 |
app:beforeMount | vueApp | 应用挂载前 | 最后的配置修改 |
app:mounted | vueApp | 应用挂载后(仅客户端) | 启动客户端逻辑 |
app:error | error | 应用错误 | 错误上报 |
page:start | - | 页面加载开始 | 显示加载条 |
page:finish | - | 页面加载完成 | 隐藏加载条 |
page:rendered | - | 页面渲染完成(SSR) | SSR 日志 |
vue:error | error, instance, info | Vue 错误 | 错误上报 |
link:prefetch | to | 链接预取 | 自定义预取策略 |
取消监听
ts
// nuxtApp.hook 返回取消函数
const unsubscribe = nuxtApp.hook('page:start', () => {
// ...
})
// 不再需要时取消
unsubscribe()TIP
大多数情况下不需要手动取消 但如果钩子注册在条件语句中或需要动态切换,取消监听可以避免内存泄漏
Nuxt Hooks
在构建时运行,主要用于模块开发和 nuxt.config.ts 中。
在 nuxt.config.ts 中使用
ts
export default defineNuxtConfig({
hooks: {
'pages:extend'(pages) {
// 动态添加页面
pages.push({
name: 'custom',
path: '/custom',
file: '~/app/pages/custom.vue',
})
},
'components:extend'(components) {
// 修改组件列表
},
},
})在模块中使用
ts
export default defineNuxtModule({
setup(options, nuxt) {
nuxt.hook('ready', () => {
console.log('Nuxt ready')
})
nuxt.hook('build:before', () => {
console.log('构建前')
})
},
})实用示例:动态添加后台管理页面
ts
export default defineNuxtConfig({
hooks: {
'pages:extend'(pages) {
// 根据环境变量决定是否添加管理页面
if (process.env.ENABLE_ADMIN === 'true') {
pages.push({
name: 'admin-dashboard',
path: '/admin',
file: '~/app/pages/admin/index.vue',
})
}
},
},
})常用 Nuxt Hooks
| 钩子 | 说明 | 典型用途 |
|---|---|---|
ready | Nuxt 就绪 | 初始化逻辑 |
build:before | 构建前 | 修改构建配置 |
build:done | 构建完成 | 生成额外文件 |
pages:extend | 扩展页面 | 动态添加/移除页面 |
components:extend | 扩展组件 | 动态添加组件 |
imports:extend | 扩展自动导入 | 添加自定义自动导入 |
imports:dirs | 添加自动导入目录 | 模块注册组合式函数 |
nitro:config | Nitro 配置 | 修改服务端配置 |
Nitro Hooks
在服务端运行,用于请求处理和服务端事件。
在服务端插件中使用
ts
export default defineNitroPlugin((nitroApp) => {
// 请求钩子
nitroApp.hooks.hook('request', (event) => {
console.log('请求:', event.path)
})
// 响应钩子
nitroApp.hooks.hook('afterResponse', (event) => {
console.log('响应:', event.path, event.node.res.statusCode)
})
// 错误钩子
nitroApp.hooks.hook('error', async (error, event) => {
console.error('服务端错误:', error)
})
// 关闭钩子
nitroApp.hooks.hook('close', () => {
console.log('服务器关闭')
})
})实用示例:请求日志中间件
ts
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('request', (event) => {
const start = Date.now()
event.context.startTime = start
})
nitroApp.hooks.hook('afterResponse', (event) => {
const duration = Date.now() - event.context.startTime
console.log(`${event.method} ${event.path} → ${duration}ms`)
})
})常用 Nitro Hooks
| 钩子 | 说明 | 典型用途 |
|---|---|---|
request | 请求到达 | 日志、鉴权 |
beforeResponse | 响应前 | 修改响应数据 |
afterResponse | 响应后 | 日志、统计 |
error | 错误发生 | 错误上报 |
close | 服务器关闭 | 清理资源 |
钩子执行时序
INFO
理解钩子的触发顺序对调试很重要 以下是一个典型的页面加载流程
text
1. Nuxt Hooks:ready ← 构建时
2. Nuxt Hooks:build:before ← 构建时
3. Nuxt Hooks:build:done ← 构建时
4. ─── 运行时 ───
5. Nitro Hooks:request ← 服务端请求到达
6. App Hooks:app:created ← Vue 应用创建
7. App Hooks:page:start ← 页面加载开始
8. App Hooks:page:rendered ← SSR 渲染完成
9. Nitro Hooks:afterResponse ← 服务端响应
10. App Hooks:app:beforeMount ← 客户端挂载前
11. App Hooks:app:mounted ← 客户端挂载后
12. App Hooks:page:finish ← 页面加载完成常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 钩子不触发 | 注册时机不对 | App Hooks 在插件中注册,Nuxt Hooks 在模块/配置中注册 |
app:mounted 在 SSR 时不触发 | 该钩子只在客户端触发 | SSR 需要的逻辑用 page:rendered |
| 钩子中异步操作报错 | 忘记 async/await | 确保异步钩子回调标记为 async |
| 错误钩子中再次抛出错误 | 导致无限循环 | 错误钩子中只做日志/上报,不要 throw |