Nuxt 生命周期

理解 Nuxt 应用程序的生命周期可以帮助你更深入地了解框架的工作原理,尤其是在服务器端和客户端渲染方面。

本章的目标是提供框架各部分的高级概览,包括它们的执行顺序及相互协作方式。

服务器端

在服务器端,每次对应用的初始请求都会执行以下步骤:

步骤 1:设置 Nitro 服务器和 Nitro 插件(仅执行一次)

Nuxt 由现代服务器引擎 Nitro 驱动。

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

  • 捕获并处理应用范围内的错误。
  • 注册在 Nitro 关闭时执行的钩子。
  • 注册请求生命周期事件的钩子,例如修改响应。
Nitro 插件仅在服务器启动时执行一次。在无服务器环境中,服务器会在每个请求到来时启动,Nitro 插件也随之执行。但它们的执行不会被等待完成。
Read more in Docs > Guide > Directory Structure > Server#server Plugins.

步骤 2:Nitro 服务器中间件

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

中间件如果返回一个值,当前请求会被终止,并以该返回值作为响应。这种行为通常应避免,以确保请求能够正常处理!
Read more in Docs > Guide > Directory Structure > Server#server Middleware.

步骤 3:初始化 Nuxt 并执行 Nuxt 应用插件

首先创建 Vue 和 Nuxt 实例。随后,Nuxt 执行其服务器端插件,包括:

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

插件按特定顺序执行,且可能相互依赖。如需更多细节,包括执行顺序及并行情况,请参见 插件文档

此步骤完成后,Nuxt 会调用 app:created 钩子,可用于执行额外逻辑。
Read more in Docs > Guide > Directory Structure > Plugins.

步骤 4:路由验证

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

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

详情见 路由验证文档

Read more in Docs > Getting Started > Routing#route Validation.

步骤 5:执行 Nuxt 应用中间件

中间件允许在导航到特定路由前运行代码,常用于身份验证、重定向或日志等。

Nuxt 中有三种类型的中间件:

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

Nuxt 在首次进入应用和每次路由导航前自动执行全局中间件。命名和匿名中间件仅在对应页面组件中的中间件元信息指定的路由上执行。

有关各类型及示例,参见 中间件文档

服务器上的任何重定向都会向浏览器发送 Location: 头部,浏览器随后对新的地址发起新请求。此时,除非状态保存在 cookie 中,所有应用状态都将被重置。

Read more in Docs > Guide > Directory Structure > Middleware.

步骤 6:设置页面和组件

Nuxt 会初始化页面及其组件,并通过 useFetchuseAsyncData 获取所需数据。由于服务端没有动态更新和 DOM 操作,诸如 onBeforeMountonMounted 等 Vue 生命周期钩子在 SSR 期间 不会 执行。

应避免在 <script setup> 的根作用域中编写需要清理副作用的代码。例如,通过 setInterval 设置定时器。在纯客户端代码中,我们可以在 onBeforeUnmountonUnmounted 钩子中清理定时器。但在 SSR 中卸载钩子不会被调用,导致定时器永远存在。要避免此问题,应将副作用代码放入 onMounted 中。

步骤 7:渲染并生成 HTML 输出

所有组件初始化和数据获取完成后,Nuxt 将组件与 unhead 的设置合并,生成完整的 HTML 文档。该 HTML 及相关数据发送给客户端,完成 SSR 过程。

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

客户端(浏览器)

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

步骤 1:初始化 Nuxt 并执行 Nuxt 应用插件

此步骤与服务器端执行类似,包括内置及自定义插件。

位于 plugins/ 目录下无后缀的插件(例如 myPlugin.ts)及带 .client 后缀的插件(例如 myClientPlugin.client.ts)会在客户端执行。

此步骤完成后,Nuxt 会调用 app:created 钩子,可用于执行额外逻辑。
Read more in Docs > Guide > Directory Structure > Plugins.

步骤 2:路由验证

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

步骤 3:执行 Nuxt 应用中间件

Nuxt 中间件既在服务器端也在客户端运行。如果你希望某些代码仅在特定环境执行,可以使用 import.meta.client(客户端)或 import.meta.server(服务器端)进行拆分。

Read more in Docs > Guide > Directory Structure > Middleware#when Middleware Runs.

步骤 4:挂载 Vue 应用及水合

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

为确保水合正确,必须保证服务器和客户端数据的一致性。对于 API 请求,建议使用 useAsyncDatauseFetch 或其他支持 SSR 的组合式函数。这些方法确保服务器端获取的数据在水合时被复用,避免重复请求。任何新的请求应在水合后触发,以防止水合错误。

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

步骤 5:Vue 生命周期

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