Lzh on GitHub

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[构建完成]

关键钩子及触发顺序

  1. 配置加载
  • config:resolved:Nuxt 配置解析完成后触发
  • 可以访问最终合并的配置对象
  1. 模块初始化
  • modules:before:模块加载前
  • modules:done:所有模块加载完成(常用插件注册点)
// nuxt.config.ts
hooks: {
  'modules:done'() {
    // 所有模块加载完成后执行
  }
}
  1. 模板生成
  • app:templates:Nuxt 核心模板生成前
  • app:templatesGenerated:模板生成完成后
hooks: {
  'app:templates'(templates) {
    // 自定义模板逻辑
  }
}
  1. 构建配置
  • vite:extendConfig:扩展 Vite 配置
  • webpack:config:修改 Webpack 配置
  • nitro:config:定制 Nitro 配置
hooks: {
  'vite:extendConfig'(config) {
    // 修改 Vite 配置
  }
}
  1. 构建过程
  • build:before:构建开始前
  • build:compile:编译开始时
  • nitro:build:before:Nitro 构建开始前
  • nitro:build:done:Nitro 构建完成
  1. 构建完成
  • 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: 返回响应

服务端钩子顺序

  1. 请求开始
  • app:created:Vue 应用实例创建后(服务端和客户端都会触发)
  1. 路由生命周期
  • app:middleware:执行全局中间件
  • page:start:开始渲染页面(仅在客户端导航时触发)
  1. 数据获取
  • app:data:refresh:刷新数据前
  • 执行 asyncDatauseFetch
  1. 渲染过程
  • vue:setup:在 setup() 函数执行前
  • app:render:渲染开始前
  • app:rendered:生成 HTML 后(可修改 HTML)
  1. 响应发送
  • app:beforeRender:发送响应前
  • app:redirected:重定向时触发
  1. 错误处理
  • 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: 获取客户端数据

客户端钩子顺序

  1. 应用挂载
  • app:created:Vue 应用实例创建
  • app:beforeMount:挂载前
  • app:mounted:挂载完成后(仅在客户端触发)
  1. 路由导航
  • page:start:路由切换开始
  • app:beforeRouteLeave:离开当前路由前
  • app:beforeRouteUpdate:路由更新前
  • page:finish:路由切换完成
  1. 数据更新
  • app:data:refresh:刷新数据时
  1. 页面生命周期
  • vue:setup:在 setup() 函数执行前
  • page:transition:finish:页面过渡完成
  1. 错误处理
  • app:error:客户端捕获错误

运行阶段特点

  • 每次请求/导航:服务端运行时针对每个请求执行,客户端运行时在页面导航时执行
  • 环境特定:服务端在 Node.js 环境,客户端在浏览器环境
  • 状态管理:Pinia store 在服务端为每个请求创建新实例
  • 典型用例:身份验证、数据获取、错误处理

三、对比总结表

特性构建阶段运行阶段
触发时机nuxt buildnuxt dev每个请求/用户交互
执行环境Node.js服务端:Node.js
客户端:浏览器
执行次数一次服务端:每次请求
客户端:每次导航
主要任务生成应用代码处理请求/响应
关键钩子modules:done, build:beforeapp: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 {
      // 客户端逻辑
    }
  })
}

五、常见误区

  1. 在构建阶段访问运行时环境变量
    // ❌ 错误:process.env 在构建时解析
    const apiUrl = process.env.API_URL
    
    // ✅ 正确:使用 runtimeConfig
    export default defineNuxtConfig({
      runtimeConfig: {
        public: {
          apiUrl: ''
        }
      }
    })
    
  2. 在服务端插件中使用客户端 API
    // ❌ 错误:document 在服务端不存在
    export default defineNitroPlugin(() => {
      document.title = 'My App'
    })
    
    // ✅ 正确:使用环境检查
    if (process.client) {
      document.title = 'My App'
    }
    
  3. 忽略服务端状态污染
    // ❌ 错误:共享状态导致安全问题
    let userToken = null
    
    // ✅ 正确:使用请求级别状态
    export default defineEventHandler((event) => {
      event.context.userToken = event.headers.get('Authorization')
    })
    

理解 Nuxt 3 的构建阶段和运行阶段生命周期对于开发高性能、可维护的应用程序至关重要。合理利用各生命周期钩子,可以更好地控制应用行为,优化性能,并避免常见陷阱。