Nuxt 生命周期

理解 Nuxt 应用的生命周期可以帮助你更深入地了解框架的运行方式,尤其是针对服务器端和客户端渲染。

本章的目标是提供框架中不同部分的高级概览,讲解它们的执行顺序以及它们如何协同工作。

服务器生命周期

在服务器端,每个初始请求到应用时,都会执行以下步骤:

服务器插件 once

Nuxt 由 Nitro 驱动,这是一个现代的服务器引擎。

当 Nitro 启动时,会初始化并执行 /server/plugins 目录下的插件。这些插件可以:

  • 捕获和处理应用范围内的错误。
  • 注册在 Nitro 关闭时执行的钩子。
  • 注册请求生命周期事件的钩子,例如修改响应。
Nitro 插件仅在服务器启动时执行一次。在无服务器(serverless)环境中,服务器会在每个传入请求时启动,Nitro 插件也会随之执行。然而,这些插件的执行不会被等待。
Docs > 4 X > Directory Structure > Server#server Plugins 中查看详情

服务器中间件

在初始化 Nitro 服务器之后,server/middleware/ 下的中间件会在每个请求上执行。中间件可用于身份认证、日志记录或请求转换等任务。

从中间件返回一个值将终止请求并将返回值作为响应发送。通常应避免这种行为以确保请求得到正确处理!
Docs > 4 X > Directory Structure > Server#server Middleware 中查看详情

应用插件

Vue 和 Nuxt 实例首先被创建。随后,Nuxt 执行其应用插件。这包括:

  • 内置插件,如 Vue Router 和 unhead
  • 位于 app/plugins/ 目录下的自定义插件,包括无后缀(例如 myPlugin.ts)和带 .server 后缀(例如 myServerPlugin.server.ts)的插件。

插件按特定顺序执行,且可能互有依赖。关于执行顺序和并行相关的详情,请参阅 插件文档

此步骤之后,Nuxt 会调用 app:created 钩子,可用于执行额外逻辑。
Docs > 4 X > Directory Structure > App > Plugins 中查看详情

路由验证

在初始化插件并且执行页面中间件之前,如果在 definePageMeta 中定义了 validate 方法,Nuxt 会调用该方法。validate 方法可以是同步或异步的,通用于验证动态路由参数。

  • 如果参数有效,validate 函数应返回 true
  • 如果验证失败,应返回 false 或包含 statusCode 和/或 statusMessage 的对象以终止请求。

更多信息请参见路由验证文档

Docs > 4 X > Getting Started > Routing#route Validation 中查看详情

应用中间件

中间件允许你在路由跳转前运行代码,常用于身份认证、重定向或日志记录等任务。

在 Nuxt 中,存在三种类型的中间件:

  • 全局路由中间件
  • 命名路由中间件
  • 匿名(或内联)路由中间件

Nuxt 会在初始页面加载时(服务器端和客户端均执行)以及之后任意客户端导航之前执行所有全局中间件。命名和匿名中间件仅在对应页面组件的路由元信息 middleware 属性指定的路由上执行。

详细类型说明及示例,请见中间件文档

在服务器端发生重定向时,会向浏览器发送 Location: 头,浏览器随后会发起对该新地址的全新请求。此时所有应用状态都会被重置,除非用 Cookie 进行了持久化。

Docs > 4 X > Directory Structure > App > Middleware 中查看详情

页面与组件

Nuxt 在此步骤中渲染页面及其组件,并使用 useFetchuseAsyncData 获取所需数据。由于服务器端不存在动态更新也不会进行 DOM 操作,Vue 生命周期钩子如 onBeforeMountonMounted 及后续钩子在 SSR 期间不会被执行。

默认情况下,为了提升性能,Vue 会在 SSR 期间暂停依赖追踪。

服务器端无响应式支持,因为 Vue SSR 会将应用自顶向下渲染成静态 HTML,渲染完成后无法修改已渲染内容。
应避免在 <script setup> 根作用域中放置需要清理的副作用代码。例如使用 setInterval 创建的定时器。客户端代码中通常在 onBeforeUnmountonUnmounted 钩子销毁这些定时器,但 SSR 期间卸载钩子不会被调用,定时器会永久存在。为避免此问题,应将副作用代码移至 onMounted 中。
观看 Daniel Roe 讲解服务器渲染和全局状态的视频。

HTML 输出

在获取全部所需数据并渲染组件后,Nuxt 会将渲染结果与 unhead 设置结合,生成完整的 HTML 文档。然后将该 HTML 及关联数据发送给客户端,完成 SSR 过程。

Vue 应用渲染为 HTML 后,Nuxt 会调用 app:rendered 钩子。
在最终决定并发送 HTML 之前,Nitro 会调用 render:html 钩子。该钩子允许操作生成的 HTML,诸如注入额外脚本或修改 meta 标签。

客户端生命周期

此部分的生命周期完全在浏览器中执行,无论你选择的是哪种 Nuxt 模式。

应用插件

此步骤与服务器端类似,包含内置插件和自定义插件。

app/plugins/ 目录下的自定义插件,如无后缀(myPlugin.ts)和带 .client 后缀(myClientPlugin.client.ts)的,会在客户端执行。

此步骤之后,Nuxt 会调用 app:created 钩子,可用于执行额外逻辑。
Docs > 4 X > Directory Structure > App > Plugins 中查看详情

路由验证

此步骤与服务器端相同,执行在 definePageMeta 中定义的 validate 方法。

应用中间件

Nuxt 中间件在服务器和客户端都会运行。如果你希望某些代码只在特定环境中运行,可以使用 import.meta.client(仅客户端)和 import.meta.server(仅服务器)区分代码。

Docs > 4 X > Directory Structure > App > Middleware#when Middleware Runs 中查看详情

挂载 Vue 应用并水合

调用 app.mount('#__nuxt') 会将 Vue 应用挂载到 DOM。如果应用使用了 SSR 或 SSG 模式,Vue 会执行水合步骤使客户端应用具有交互性。在水合期间,Vue 会重新创建应用(不包括 服务器组件),匹配每个组件与其对应的 DOM 节点,并附加 DOM 事件监听器。

为确保水合正确,需要保持服务器端和客户端数据一致。针对 API 请求,推荐使用 useAsyncDatauseFetch 或其他 SSR 友好的组合式函数。这些方法确保服务器端获取的数据在水合期间可以复用,避免重复请求。任何新的请求应在水合完成后触发,以防止水合失败。

挂载 Vue 应用之前,Nuxt 会调用 app:beforeMount 钩子。
挂载 Vue 应用之后,Nuxt 会调用 app:mounted 钩子。

Vue 生命周期

与服务器端不同,浏览器会执行完整的 Vue 生命周期