首页 / Nuxt 4 入门教程 / 生命周期与 Nuxt 钩子

Nuxt 4 入门教程

生命周期与 Nuxt 钩子

本教程共 50 篇 · 第 40 篇 · 更新于 2026-08-08 · 约 8 分钟阅读

NuxtNuxt4生命周期hooks钩子NuxtApp

本节目标:理解 Nuxt 应用在服务端和客户端分别经历了哪些阶段,并学会用 hooks 在关键节点插入自己的逻辑。

40-1

你写的代码不是「一瞬间全跑完」的。一个 Nuxt 页面从用户请求到能在浏览器里点击,中间要经历:启动服务器、初始化应用、跑插件、执行中间件、渲染页面、把 HTML 发回浏览器、浏览器再激活成可交互页面。了解这条时间线,你才知道「某段代码到底什么时候能跑、为什么有时候跑不了」。

可以把生命周期想象成一条流水线:每个工位(钩子)都会广播一声「我到这一步了」,你就能挂个监听器上去干点事。

40-2

在服务器上,每个请求会依次经历这些步骤:

  1. 启动 Nitro 服务器与 Nitro 插件(一次性)。Nitro 是 Nuxt 的服务端引擎,它先初始化并跑 server/plugins 里的插件。在无服务器环境里,每次冷启动都会执行一次。
  2. Nitro 服务器中间件server/middleware/ 对每个请求执行,常用于鉴权、日志、改写请求。
  3. 初始化 Nuxt 并执行应用插件。创建 Vue 和 Nuxt 实例,跑内置插件(Vue Router、Unhead)和 app/plugins 里的自定义插件。之后触发 app:created 钩子。
  4. 路由验证。如果页面用 definePageMeta 定义了 validate,在这里校验动态参数。
  5. 执行应用中间件。全局/命名/匿名中间件,常用于鉴权重定向。
  6. 渲染页面和组件。用 useFetch/useAsyncData 取数据。注意:SSR 阶段不会执行 onMounted 这类客户端生命周期钩子,因为服务端只产出静态 HTML,没有 DOM 更新。
  7. 生成 HTML 输出。结合 Unhead 生成完整 HTML 发回浏览器,并触发 app:renderedrender:html 钩子。
Note

SSR 阶段没有「响应式」和「DOM 操作」:HTML 是自上而下一次性渲染出来的。所以要避免在 <script setup> 根作用域写带清理的副作用(比如 setInterval 计时器)——服务端不会调用卸载钩子,计时器会一直泄漏。把这类代码放到 onMounted 里。

40-3

浏览器拿到 HTML 后,Vue 会「激活」它,让它变可交互:

  1. 初始化 Nuxt 并执行应用插件(与服务端类似,但 .client 插件在此执行)。
  2. 路由验证应用中间件同样会跑一遍。
  3. 挂载并激活。调用 app.mount('#__nuxt'),Vue 把服务端 HTML 重新「对上号」、绑定事件监听。此时触发 app:beforeMountapp:mounted 钩子。
  4. 完整 Vue 生命周期。这时 onMountedonUpdated 等才真正执行。
Tip

服务端和客户端都跑中间件、插件。如果你希望某段逻辑只在特定环境执行,用 import.meta.client / import.meta.server 区分,别靠「应该只在浏览器跑」的直觉。

40-4

Nuxt 的钩子系统由 unjs/hookable 驱动,分三类:

构建时钩子(Nuxt hooks):在 nuxt.config 或模块里用,影响构建流程。

export default defineNuxtConfig({
  hooks: {
    close: () => { /* 构建收尾 */ },
  },
})

应用运行时钩子(App hooks):在插件或组合式函数里挂到 nuxtApp,用于页面渲染周期。

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('page:start', () => {
    // 页面开始切换
  })
  nuxtApp.hook('page:finish', () => {
    // 页面切换完成
  })
})

服务端钩子(Server hooks):在 server/plugins 里挂到 Nitro,用于服务端渲染行为。

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('render:html', (html, { event }) => {
    html.bodyAppend.push('<hr>由自定义插件追加')
  })
})

40-5

除了插件,组合式函数里也能拿到 nuxtApp 并挂钩子。组件卸载时记得解绑,避免内存泄漏:

export const usePageStart = (cb: () => void) => {
  const nuxtApp = useNuxtApp()
  const stop = nuxtApp.hook('page:start', cb)
  onUnmounted(() => stop())
}

40-6

你甚至能定义自己的钩子,扩展 Nuxt 的接口,方便团队内部约定:

import type { HookResult } from '@nuxt/schema'

declare module '#app' {
  interface RuntimeNuxtHooks {
    'my-event': (payload: string) => HookResult
  }
}
Warning

钩子里不要做阻塞主流程的重活。page:start 这类渲染相关钩子若执行太久,会拖慢页面切换。耗时任务挪到后台或 onMounted 之后。

40-7

Nuxt 3 与 Nuxt 4 的生命周期与钩子体系一致:Nitro 启动 → 应用插件 → 中间件 → 渲染/激活的链路相同,nuxtApp.hookdefineNitroPlugin 用法不变。

40-8

除了前面举例的 page:start/page:finish,日常还有几个高频应用钩子值得记住:

  • app:created:Nuxt 应用实例刚建好,插件已跑完。
  • app:beforeMount / app:mounted:客户端挂载前后。
  • app:rendered:SSR 渲染出 HTML 之后。
  • vue:error:任意 Vue 错误冒泡到顶层(第 41 章用它做全局上报)。

这些钩子都挂在 nuxtApp.hook('名字', 回调) 上。记住:渲染相关的钩子(page:*app:rendered)在客户端、服务端都会触发,回调里若用了浏览器 API,要用 import.meta.client 包一下。

Note

想看完整钩子清单,去 nuxt.com/docsnuxtApp 钩子。先记住上面这几个,足够应付大多数「页面切换埋点、加载动画」的需求。

40-9

写钩子时还有几个容易忽略的细节。第一,回调参数是「按需取」的:page:start 没有参数,page:finish 会带上路由对象,别凭记忆硬写参数名,不确定就去文档对一下。第二,钩子函数可以返回 Promise,Nuxt 会等它 resolve 再继续——这点在 app:mounted 里做异步初始化时很有用,但也意味着别在里面 await 一个永远不结束的 Promise,否则页面会卡在加载态。第三,插件里注册的钩子大多不必手动清理,但如果你在钩子里开了定时器、订阅了外部事件,要在对应的清理点关掉,避免内存泄漏。

40-10

Nuxt 一条请求要走过 Nitro 启动、插件、中间件、渲染、HTML 输出(服务端),再到挂载激活(客户端)。三类钩子分别作用于构建时、应用运行时、服务端。下一章我们把「出错」这件事讲透:错误处理与自定义错误页。

40-9 钩子的错误处理与调试

钩子回调里的错误如果不处理,可能导致应用静默失败。建议在钩子回调里加 try-catch,至少把错误打印到日志。对于关键钩子(如 app:error),还应该接入监控告警系统,确保问题能被及时发现。

调试钩子的一个实用技巧是在回调里加 console.log,打印钩子名称和关键参数。这能帮你确认钩子是否被触发、触发时机是否正确、传入的数据是否符合预期。在开发环境下,Nuxt 的终端日志会显示构建钩子的执行情况。

如果你发现某个钩子没有按预期触发,检查几个常见原因:钩子名称拼写是否正确、注册时机是否太晚(比如在插件里注册的钩子可能错过了构建阶段的钩子)、钩子所属的模块或插件是否被正确加载。

40-10 生命周期相关的调试工具

Nuxt DevTools 为生命周期调试提供了可视化支持。在 DevTools 里,你可以看到各个钩子的触发时机、执行耗时,甚至可以在钩子回调里设置断点。这比传统的 console.log 调试方式高效得多。

在服务端,Nitro 的日志系统也能帮助你追踪请求生命周期中各个钩子的执行情况。通过调整日志级别,你可以看到从请求进入到响应返回的完整链路,包括中间经过了哪些中间件和钩子。

当钩子的执行顺序不符合预期时,检查几个方面:插件的注册顺序是否正确、模块的加载顺序是否影响了钩子注册、是否有异步操作导致钩子回调延迟执行。理清这些问题,钩子的行为就能被准确预测。