Nuxt 扩展点
了解 Nuxt 应用程序从构建到运行时如何扩展
Nuxt 3 生命周期详解:构建阶段 vs 运行阶段
Nuxt 3 的生命周期分为构建阶段(Build Time)和运行阶段(Runtime),每个阶段都有特定的生命周期钩子和执行时机。下面我将详细解释这两个阶段的区别、触发顺序和典型用例。
一、构建阶段(Build Time)
构建阶段发生在执行 nuxt build 或开发服务器启动时,主要任务是生成客户端和服务端代码。
构建阶段生命周期流程
graph TD
A[配置加载] --> B[模块初始化]
B --> C[模板生成]
C --> D[客户端构建]
D --> E[Nitro 服务端构建]
E --> F[构建完成]
关键钩子及触发顺序
- 配置加载
config:resolved:Nuxt 配置解析完成后触发- 可以访问最终合并的配置对象
- 模块初始化
modules:before:模块加载前modules:done:所有模块加载完成(常用插件注册点)
// nuxt.config.ts
hooks: {
'modules:done'() {
// 所有模块加载完成后执行
}
}
- 模板生成
app:templates:Nuxt 核心模板生成前app:templatesGenerated:模板生成完成后
hooks: {
'app:templates'(templates) {
// 自定义模板逻辑
}
}
- 构建配置
vite:extendConfig:扩展 Vite 配置webpack:config:修改 Webpack 配置nitro:config:定制 Nitro 配置
hooks: {
'vite:extendConfig'(config) {
// 修改 Vite 配置
}
}
- 构建过程
build:before:构建开始前build:compile:编译开始时nitro:build:before:Nitro 构建开始前nitro:build:done:Nitro 构建完成
- 构建完成
build:manifest:构建清单生成后build:done:整个构建过程完成
构建阶段特点
- 只执行一次:在开发服务器启动或生产构建时执行
- Node.js 环境:在服务器环境中运行
- 生成产物:创建
.nuxt和.output目录 - 典型用例:修改构建配置、注册全局组件、生成路由模板
二、运行阶段(Runtime)
运行阶段发生在应用程序实际运行时,分为服务端运行时(Server Runtime)和客户端运行时(Client Runtime)。
服务端运行时生命周期(Server Runtime)
sequenceDiagram
participant Request as HTTP 请求
participant Server as Nuxt 服务端
participant Vue as Vue 应用
Request->>Server: 接收请求
Server->>Server: 执行 server 插件
Server->>Server: 运行路由中间件
Server->>Server: 执行 asyncData/fetch
Server->>Vue: 创建 Vue 应用
Vue->>Vue: SSR 渲染组件
Server->>Server: 生成 HTML
Server->>Request: 返回响应
服务端钩子顺序
- 请求开始
app:created:Vue 应用实例创建后(服务端和客户端都会触发)
- 路由生命周期
app:middleware:执行全局中间件page:start:开始渲染页面(仅在客户端导航时触发)
- 数据获取
app:data:refresh:刷新数据前- 执行
asyncData或useFetch
- 渲染过程
vue:setup:在setup()函数执行前app:render:渲染开始前app:rendered:生成 HTML 后(可修改 HTML)
- 响应发送
app:beforeRender:发送响应前app:redirected:重定向时触发
- 错误处理
app:error:捕获错误
客户端运行时生命周期(Client Runtime)
sequenceDiagram
participant Browser as 浏览器
participant Vue as Vue 应用
Browser->>Browser: 加载 HTML/CSS/JS
Browser->>Vue: 挂载 Vue 应用
Vue->>Vue: 执行客户端插件
Vue->>Vue: 客户端激活 (hydration)
Browser->>Browser: 用户交互
Browser->>Vue: 路由导航
Vue->>Vue: 执行路由守卫
Vue->>Vue: 获取客户端数据
客户端钩子顺序
- 应用挂载
app:created:Vue 应用实例创建app:beforeMount:挂载前app:mounted:挂载完成后(仅在客户端触发)
- 路由导航
page:start:路由切换开始app:beforeRouteLeave:离开当前路由前app:beforeRouteUpdate:路由更新前page:finish:路由切换完成
- 数据更新
app:data:refresh:刷新数据时
- 页面生命周期
vue:setup:在setup()函数执行前page:transition:finish:页面过渡完成
- 错误处理
app:error:客户端捕获错误
运行阶段特点
- 每次请求/导航:服务端运行时针对每个请求执行,客户端运行时在页面导航时执行
- 环境特定:服务端在 Node.js 环境,客户端在浏览器环境
- 状态管理:Pinia store 在服务端为每个请求创建新实例
- 典型用例:身份验证、数据获取、错误处理
三、对比总结表
| 特性 | 构建阶段 | 运行阶段 |
|---|---|---|
| 触发时机 | nuxt build 或 nuxt dev | 每个请求/用户交互 |
| 执行环境 | Node.js | 服务端:Node.js 客户端:浏览器 |
| 执行次数 | 一次 | 服务端:每次请求 客户端:每次导航 |
| 主要任务 | 生成应用代码 | 处理请求/响应 |
| 关键钩子 | modules:done, build:before | app:created, app:rendered |
| 典型操作 | 配置构建工具 注册插件 | 身份验证 数据获取 错误处理 |
| 状态管理 | 全局状态 | 请求/会话级别状态 |
四、最佳实践
1. 构建阶段
// nuxt.config.ts
export default defineNuxtConfig({
hooks: {
'modules:done'() {
// 所有模块加载完成后添加插件
},
'vite:extendConfig'(config) {
// 扩展 Vite 配置
}
}
})
2. 服务端运行时
// server/plugins/auth.ts
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('request', (event) => {
// 每个请求前验证身份
})
})
3. 客户端运行时
// plugins/client-init.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('app:mounted', () => {
// 应用挂载后执行
console.log('App mounted in client')
})
})
4. 共享逻辑
// composables/useSharedLogic.ts
export const useSharedLogic = () => {
const nuxtApp = useNuxtApp()
// 在生命周期钩子中执行
nuxtApp.hook('app:created', () => {
if (process.server) {
// 服务端逻辑
} else {
// 客户端逻辑
}
})
}
五、常见误区
- 在构建阶段访问运行时环境变量
// ❌ 错误:process.env 在构建时解析 const apiUrl = process.env.API_URL // ✅ 正确:使用 runtimeConfig export default defineNuxtConfig({ runtimeConfig: { public: { apiUrl: '' } } }) - 在服务端插件中使用客户端 API
// ❌ 错误:document 在服务端不存在 export default defineNitroPlugin(() => { document.title = 'My App' }) // ✅ 正确:使用环境检查 if (process.client) { document.title = 'My App' } - 忽略服务端状态污染
// ❌ 错误:共享状态导致安全问题 let userToken = null // ✅ 正确:使用请求级别状态 export default defineEventHandler((event) => { event.context.userToken = event.headers.get('Authorization') })
理解 Nuxt 3 的构建阶段和运行阶段生命周期对于开发高性能、可维护的应用程序至关重要。合理利用各生命周期钩子,可以更好地控制应用行为,优化性能,并避免常见陷阱。