跳到主要内容

Nuxt 生命周期

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

本章的目标是,从高层视角概述框架的各个组成部分、它们的执行顺序,以及它们如何协同工作。

服务端生命周期

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

服务端插件 once

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

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

  • 捕获并处理应用级别的运行时错误。
  • 注册在 Nitro 关闭时执行的钩子。
  • 为请求生命周期事件(例如修改响应)注册钩子。
Nitro 插件只在服务器启动时执行一次。在无服务器环境中,服务器会在每次传入请求时启动,Nitro 插件也是如此。不过,它们并不会被等待(awaited)。

服务端中间件

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

从中间件返回一个值会终止请求,并将该返回值作为响应发送。一般来说应该避免这种行为,以确保请求被正确处理!

应用插件(App plugins)

首先创建 Vue 和 Nuxt 实例。之后,Nuxt 执行它的应用插件。这包括:

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

插件以特定的顺序执行,并且可能彼此存在依赖。更多关于执行顺序和并行性的细节,请参阅 插件文档

在此步骤之后,Nuxt 调用 app:created 钩子,可用于执行额外的逻辑。

路由校验(Route validation)

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

  • 如果参数有效,validate 函数应返回 true
  • 如果校验失败,它应返回 false,或一个包含 status 和/或 statusText 的对象来终止请求。

更多信息,请参阅 路由校验文档

应用中间件(App middleware)

中间件允许你在导航到某个特定路由之前运行代码。它常用于认证、重定向或日志记录等任务。

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

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

Nuxt 在初始页面加载时(服务端和客户端都会)执行所有全局中间件,然后在任何客户端导航之前再次执行。具名和匿名中间件只在对应页面组件中定义的页面(路由)meta 的 middleware 属性所指定的路由上执行。

关于每种类型及示例的细节,请参阅 中间件文档

在服务端发生的任何重定向,都会向浏览器发送一个 Location: 头;随后浏览器会向这个新位置发起一个全新的请求。发生这种情况时,除非持久化在 cookie 中,否则所有应用状态都会被重置。

页面与组件

Nuxt 在此步骤渲染页面及其组件,并使用 useFetchuseAsyncData 获取所需的任何数据。由于服务端没有动态更新、也不会发生 DOM 操作,像 onBeforeMountonMounted 这样的 Vue 生命周期钩子以及后续的钩子在 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 钩子,可用于执行额外的逻辑。

路由校验

此步骤与服务端执行相同,包含 definePageMeta 函数中定义的 validate 方法(如果定义)。

应用中间件

Nuxt 中间件运行在服务端和客户端。如果你希望某些代码只在特定环境运行,考虑通过使用 import.meta.client 用于客户端、import.meta.server 用于服务端来拆分它。

挂载 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 生命周期