本文基于 Nuxt 3(当前主流版本),兼顾 Nuxt 2 的核心差异点。目标是让你读完后,能回答面试中关于 Nuxt 的绝大多数问题。
第一章 Nuxt.js 的定位与核心概念
1.1 Nuxt 是什么
Nuxt.js 是一个基于 Vue.js 的元框架(Meta-Framework)。所谓"元框架",是指它在 Vue(一个 UI 框架)之上,提供了完整的应用工程方案:路由、服务端渲染、数据获取、构建优化、部署适配等开箱即用的能力。类比关系:Vue 之于 Nuxt,如同 React 之于 Next.js,Svelte 之于 SvelteKit。
Nuxt 解决的本质问题是:把一个前端框架(Vue)变成一个全栈应用框架。Vue 本身只关心组件渲染,而 Nuxt 在其上叠加了路由系统、服务端/客户端渲染协调、数据序列化与水合(hydration)、代码分割策略、模块生态等企业级需求。
1.2 Nuxt 3 与 Nuxt 2 的关键差异
| 维度 | Nuxt 2 | Nuxt 3 |
|---|---|---|
| 底层服务器 | @nuxt/server (Connect) | Nitro (h3 + Rollup) |
| Vue 版本 | Vue 2 | Vue 3 |
| 构建工具 | Webpack | Vite(默认)/ Webpack |
| 响应式 | Options API | Composition API |
| 语言 | JavaScript | TypeScript(原生支持) |
| 包管理 | nuxt 单体包 | 模块化拆分为多个独立包 |
| 服务端引擎 | 紧耦合的 Express/Connect | 与平台无关的 Nitro(可部署到 Cloudflare Workers、Deno Deploy 等) |
| 数据获取 | asyncData()、fetch() (Options API) | useAsyncData()、useFetch() (Composition API) |
| 状态管理 | Vuex | Pinia / useState |
| 混合渲染 | 不支持 | Route Rules(per-route SSR/SPA/ISR) |
1.3 核心概念速览
约定优于配置(Convention over Configuration):Nuxt 的核心设计哲学。通过目录结构和文件命名约定来自动生成功能,减少手动配置。例如 pages/ 目录下的文件自动生成路由,server/api/ 下的文件自动注册为 API 端点。
全栈一体:同一个项目中既有前端 Vue 应用,也有后端 Node.js(或 Serverless)服务,共享类型定义和配置。
通用渲染(Universal Rendering):同一套 Vue 组件代码既能在服务端渲染为 HTML,也能在客户端接管交互,Nuxt 负责协调两者之间的状态同步。
第二章 整体架构全景
2.1 架构分层总览
Nuxt 3 的架构可以分为四个核心层:
┌─────────────────────────────────────────────────────────┐ │ 开发者代码层 │ │ pages/ layouts/ components/ server/ plugins/ │ ├─────────────────────────────────────────────────────────┤ │ Nuxt 框架层 │ │ Nuxt Core · Nuxt Kit · @nuxt/schema │ │ 自动导入 · 模块系统 · 插件系统 · 中间件系统 │ ├─────────────────────────────────────────────────────────┤ │ 引擎层 │ │ Nitro (Server Engine) · Vite (Build Engine) │ │ h3 (HTTP Framework) · vue-bundle-renderer │ ├─────────────────────────────────────────────────────────┤ │ 平台/运行时层 │ │ Node.js · Cloudflare Workers · Deno · Vercel Edge │ │ AWS Lambda · Netlify Functions · Static Hosting │ └─────────────────────────────────────────────────────────┘
2.2 核心包关系图
Nuxt 3 本身是一个由多个 npm 包组成的 monorepo(仓库地址 nuxt/nuxt),核心包包括:
nuxt(主入口包)—— 这是用户安装的包,它实际上是一个编排器,内部依赖并协调以下子包:
@nuxt/kit —— 模块和插件开发的工具库。提供了 defineNuxtModule、useNuxt()、addPlugin()、addComponent()、addServerHandler() 等 API。所有 Nuxt 模块都通过 @nuxt/kit 与 Nuxt 核心交互。
@nuxt/schema —— 定义了 Nuxt 的完整配置 schema(NuxtConfig 类型就是从这里生成的)。它使用 untyped 库从 schema 自动推导 TypeScript 类型,所以用户在 nuxt.config.ts 中获得完整的类型提示。
@nuxt/vite-builder —— 基于 Vite 的构建器,负责 Vue 应用的开发服务器和生产构建。
@nuxt/webpack-builder —— 基于 Webpack 的可选构建器(Nuxt 3 默认使用 Vite)。
@nuxt/vue-renderer —— 负责将 Vue 应用渲染为 HTML(SSR)或生成 SPA 的 HTML 壳。
Nitro 相关包(nitropack) —— 独立的 HTTP 服务器引擎,有自己的构建管线,最终可以编译为各种平台的部署产物。
2.3 Nuxt 的生命周期(构建时 vs 运行时)
理解 Nuxt 最关键的一点是区分构建时(Build Time)和运行时(Runtime)两个阶段:
构建时(nuxi build / nuxi dev):
- 加载
nuxt.config.ts配置 - 初始化 Nuxt 实例(创建
NuxtApp对象——注意这是构建时的内部对象,不是运行时的) - 安装所有模块(Modules),按顺序执行每个模块的 setup 函数
- 扫描项目目录(pages、components、composables、plugins、middleware、server/api 等)
- 通过模板引擎生成虚拟文件(
.nuxt/目录下的文件,如app.config.mjs、routes.mjs、plugins/server.mjs等) - 构建 Nitro 服务器应用
- 构建 Vue 客户端和服务端应用(通过 Vite 或 Webpack)
- 输出部署产物(
.output/目录)
运行时(node .output/server/index.mjs):
- Nitro 服务器启动,监听 HTTP 请求
- 根据请求 URL 匹配渲染模式(SSR/SPA/ISR/SWR 等,由 Route Rules 决定)
- 如果是 SSR:在服务端执行 Vue SSR 渲染,生成 HTML + payload
- 返回 HTML 给浏览器
- 浏览器加载客户端 JS bundle
- Vue 客户端应用挂载(mount),执行水合(hydration),接管 DOM
- 客户端路由切换时,通过 payload 或 API 调用获取数据
第三章 构建管线深度剖析
3.1 Nuxt 初始化流程
当你运行 npx nuxi dev 或 npx nuxi build 时,内部发生的事情:
用户执行 nuxi dev
│
▼
CLI 入口 (nuxi)
│
▼
加载 nuxt.config.ts
(使用 jiti 执行 TypeScript 配置文件)
│
▼
创建 Nuxt 实例
(调用 createNuxt(options))
│
▼
┌─────────────────────────┐
│ Nuxt 实例内部结构 │
│ - options (完整配置) │
│ - hooks (钩子系统) │
│ - vfs (虚拟文件系统) │
│ - server (Nitro引用) │
└─────────────────────────┘
│
▼
安装所有模块 (installModules)
按 nuxt.config 中 modules 数组的顺序
依次调用每个模块的 setup()
│
▼
执行 Nuxt 钩子
(modules:done → app:resolve → build:before → ...)
│
▼
扫描项目目录
(pages/, components/, composables/, plugins/, middleware/)
│
▼
生成 .nuxt/ 虚拟文件
(路由表、插件注册、组件注册等)
│
▼
启动开发服务器 (Vite dev server + Nitro dev server)
或执行生产构建 (Vite build + Nitro build)3.2 虚拟文件生成(.nuxt 目录)
Nuxt 在构建时会生成一个 .nuxt/ 目录,里面是自动生成的代码文件。这些文件是 Nuxt "约定优于配置" 的核心实现手段。关键文件包括:
.nuxt/app.config.mjs —— 合并用户 app.config.ts 和模块提供的配置。
.nuxt/components.plugin.mjs —— 注册所有自动发现的组件(来自 components/ 目录及模块注册的组件),使用 app.component() 全局注册。
.nuxt/imports.d.ts —— 自动导入的类型声明文件。Nuxt 使用 unimport 扫描 composables/ 目录和内置 composables,生成这个文件以提供 TypeScript 支持。
.nuxt/middleware/ —— 中间件注册相关代码。
.nuxt/nuxt.config.mjs —— 运行时的配置对象(注意与构建时的 nuxt.config.ts 不同,这是经过处理和 tree-shaking 后的精简版本)。
.nuxt/plugins/ —— 插件注册代码,分为 client 和 server 两个版本,控制插件的执行顺序和环境。
.nuxt/routes.mjs —— 基于 pages/ 目录结构自动生成的路由配置。这是 vue-router 的路由数组。
.nuxt/types/ —— 自动生成的 TypeScript 类型,包括路由类型、组件类型、布局类型等。
.nuxt/dist/ —— 构建产物(生产模式下)。
3.3 Vite 构建管线
Nuxt 3 默认使用 Vite 作为前端构建工具。构建过程分为两个独立的构建:
客户端构建(Client Build):
- 入口:
.nuxt/dist/client/中的入口文件 - 产出:浏览器可执行的 JS/CSS/资源文件
- 代码分割策略:每个路由页面自动分割为独立 chunk(lazy loading)
- 产出位于
.output/public/_nuxt/
服务端构建(Server Build):
- 入口:
.nuxt/dist/server/中的入口文件 - 产出:Node.js 可执行的 bundle(用于 SSR 渲染)
- 这是一个特殊的 bundle:它不包含 Node.js 内置模块和外部依赖(externals)
- 产出位于
.output/server/
在开发模式下,Vite 的开发服务器提供 HMR(Hot Module Replacement),Nitro 的开发服务器则负责 SSR 渲染和 API 路由。
3.4 自动导入系统(Auto-imports)
这是 Nuxt 3 最具特色的功能之一,底层依赖 unimport 和 unplugin-auto-import。
工作原理:
- Nuxt 在构建时扫描以下来源的导出:
- Vue 的核心 API(
ref、computed、watch、onMounted等) - Vue Router 的 API(
useRoute、useRouter、navigateTo等) - Nuxt 内置的 composables(
useFetch、useAsyncData、useState、useHead、useRuntimeConfig等) - 用户
composables/目录下的所有导出 - 用户
utils/目录下的所有导出 - 模块注册的 composables
- Vue 的核心 API(
- 生成一个 import map,记录每个导出的来源文件
- 在构建时,通过 Vite/Rollup 插件对用户的源码进行 AST 分析:
- 发现代码中使用了
ref但没有 import → 自动在文件头部插入import { ref } from 'vue' - 发现代码中使用了
useMyComposable→ 自动插入import { useMyComposable } from '~/composables/useMyComposable'
- 发现代码中使用了
- 同时生成
.nuxt/types/imports.d.ts提供 IDE 类型支持
面试关键点:自动导入不是魔法,它是构建时的静态分析 + 代码转换。它不会增加运行时开销,因为最终产物里就是普通的 ES module import。
3.5 组件自动注册
components/ 目录下的 Vue 组件会被自动注册。实现机制:
- Nuxt 扫描
components/目录(包括子目录和模块注册的组件路径) - 为每个组件生成注册信息(名称、路径、是否异步加载等)
- 在
.nuxt/components.plugin.mjs中,使用app.component()全局注册 - 对于懒加载组件,使用
defineAsyncComponent包装,配合 Vite 的代码分割实现按需加载 - 组件名称基于文件路径自动生成:
components/ui/Button.vue→<UiButton />
第四章 Nitro 服务器引擎
Nitro 是 Nuxt 3 最核心的架构创新。它不仅仅是一个服务器——它是一个与平台无关的 HTTP 应用引擎。
4.1 Nitro 的设计理念
传统的 Nuxt 2 服务器紧耦合于 Node.js 和 Connect/Express 中间件模型。这导致了一个问题:如果你想部署到 Cloudflare Workers(基于 V8 Isolates,不支持 Node.js API),或者 AWS Lambda(Serverless 模型),需要大量的适配工作。
Nitro 的设计目标是:
- 一次编写,到处部署:同一份服务器代码可以部署到 Node.js、Cloudflare Workers、Deno Deploy、Vercel Edge Functions、AWS Lambda、Netlify Functions 等平台
- 基于 Web Standards:使用
Request/Response(Fetch API)作为请求/响应的基础抽象,而非 Node.js 的IncomingMessage/ServerResponse - 零配置优化:自动 tree-shaking 未使用的代码,最小化部署体积
- 跨平台兼容层:通过
unenv包提供 Node.js API 的 polyfill/shim
4.2 h3:Nitro 的 HTTP 框架
Nitro 底层使用 h3 作为 HTTP 框架。h3(名字来源于 H₂O 的"三氢"——比 H₂O 多一个 H,寓意比传统 HTTP 框架多一点东西)是一个极简的、兼容 Node.js 和 Web Standards 的 HTTP 框架。
h3 的核心数据结构:
// H3Event —— 封装了每个 HTTP 请求的上下文
interface H3Event {
// 底层的 Node.js 请求/响应对象(Node.js 环境)
node: {
req: IncomingMessage
res: ServerResponse
}
// Web Standards 的 Request/Response(跨平台环境)
web?: {
request: Request
}
// 请求上下文,可以存储任意数据
context: Record<string, any>
// 请求的 Fetch API Request 对象
_request?: Request
}关键 API:
// 读取请求体
const body = await readBody(event) // JSON body
const formData = await readFormData(event) // form data
const rawBody = await readRawBody(event) // raw buffer
// 读取查询参数
const query = getQuery(event) // ?key=value → { key: 'value' }
// 读取路由参数
const params = getRouterParams(event) // /api/user/:id → { id: '...' }
// 设置响应头/状态码
setResponseHeader(event, 'Content-Type', 'application/json')
setResponseStatus(event, 201)
// 发送响应
return { data: '...' } // 自动 JSON 序列化
return send(event, 'Hello') // 发送字符串
return sendStream(event, readableStream) // 流式响应
// 错误处理
throw createError({
statusCode: 404,
statusMessage: 'Not Found',
data: { id: '...' }
})4.3 Nitro 的服务器目录约定
server/
├── api/ # API 路由(自动注册为 /api/* 端点)
│ ├── hello.ts # GET /api/hello
│ ├── hello.post.ts # POST /api/hello(通过文件名后缀指定 HTTP 方法)
│ └── users/
│ ├── index.ts # GET /api/users
│ ├── [id].ts # GET /api/users/:id(动态路由参数)
│ └── [id].put.ts # PUT /api/users/:id
├── routes/ # 自定义路由(不自动加 /api 前缀)
│ └── health.ts # GET /health
├── middleware/ # 服务器中间件(在每个请求前执行)
│ ├── auth.ts # 认证中间件
│ └── logger.ts # 日志中间件
├── plugins/ # Nitro 插件(服务器启动时执行一次)
│ └── database.ts # 数据库连接初始化
└── utils/ # 服务器工具函数(自动导入)
└── validators.tsAPI 路由的处理函数签名:
// server/api/hello.ts
export default defineEventHandler(async (event: H3Event) => {
// event 是 h3 的事件对象,包含了请求的所有信息
// 读取查询参数
const query = getQuery(event)
// 读取请求体
const body = await readBody(event)
// 返回数据(自动序列化为 JSON)
return {
message: 'Hello World',
query
}
})中间件的签名:
// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
// 中间件可以修改 event.context,后续的处理函数可以读取
event.context.userId = await verifyToken(getHeader(event, 'Authorization'))
// 如果中间件返回了值,就直接作为响应返回,不再执行后续处理
// 如果不返回值(return undefined),则继续执行后续处理
})4.4 Nitro 的构建与部署
Nitro 的构建过程:
- 收集处理器(Handlers):扫描
server/api/、server/routes/、server/middleware/,加上 Nuxt 内置的 SSR 渲染处理器 - 构建 Rollup Bundle:使用 Rollup 打包服务器代码
- 平台适配:根据
preset配置选择目标平台的入口模板(Node.js、Cloudflare Workers、AWS Lambda 等) - 静态资源处理:将
public/和客户端构建产物复制到输出目录 - 生成部署产物:输出到
.output/目录
部署产物结构:
.output/ ├── server/ │ ├── index.mjs # 服务器入口(可直接 node index.mjs 启动) │ ├── chunks/ # 代码分割后的 chunk 文件 │ └── plugins/ # Nitro 插件 ├── public/ # 静态资源 │ ├── _nuxt/ # 客户端构建产物(JS/CSS/字体等) │ ├── favicon.ico │ └── ... ├── nitro.json # Nitro 元数据 └── package.json # 精简的 package.json(只包含运行时依赖)
Route Rules(路由规则)—— Nitro 最强大的特性之一:
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// 这个页面使用 SPA 模式(不 SSR)
'/admin/**': { ssr: false },
// 这个页面静态生成(构建时生成 HTML)
'/blog': { prerender: true },
// 这个 API 使用 ISR(增量静态再生成),缓存 1 小时
'/api/popular': { isr: 3600 },
// 这个页面使用 SWR(stale-while-revalidate),缓存 10 分钟
'/products/**': { swr: 600 },
// 这个路径重定向
'/old-page': { redirect: '/new-page' },
// 添加自定义响应头
'/api/**': {
headers: { 'X-Custom-Header': 'value' }
}
}
})这意味着在同一个 Nuxt 应用中,不同路由可以使用不同的渲染策略。这在实际项目中非常实用:管理后台用 SPA(因为需要登录态),营销页面用静态生成(追求性能和 SEO),数据 API 用 ISR/SWR(平衡性能和实时性)。
第五章 Nuxt 应用层架构
5.1 应用入口与启动流程
Nuxt 客户端应用的启动过程(运行时):
浏览器加载 HTML
│
▼
加载客户端 JS Bundle
│
▼
执行 Nuxt 入口文件
(.nuxt/dist/client/app.config.mjs → nuxt/dist/app/)
│
▼
创建 Vue App 实例
(createApp() 或 createSSRApp())
│
▼
创建 NuxtApp 实例
(运行时 NuxtApp,包含插件、钩子、payload 等)
│
▼
安装 Vue 插件
(router、pinia、全局组件等)
│
▼
执行 Nuxt 插件
(按顺序执行 plugins/ 目录下的插件)
│
▼
挂载 Vue App 到 DOM
(app.mount('#__nuxt'))
│
▼
水合(Hydration)
(将服务端渲染的静态 HTML "激活"为可交互的 Vue 应用)5.2 运行时 NuxtApp 数据结构
这是 Nuxt 3 运行时最核心的数据结构。通过 useNuxtApp() 可以在任何组件或 composable 中访问:
interface NuxtApp {
// Vue 应用实例
vueApp: App<Element>
// 全局属性(类似 Vue 2 的 Vue.prototype)
globalName: string // 默认 'nuxt'
// 版本信息
versions: Record<string, string>
// 钩子系统
hooks: Hookable<NuxtAppHooks>
hook: NuxtApp['hooks']['hook']
callHook: NuxtApp['hooks']['callHook']
// 运行回调(注册在 app 挂载/渲染时执行的回调)
_asyncDataPromises: Record<string, Promise<any>>
// SSR 上下文(仅在服务端存在)
ssrContext?: NuxtSSRContext
// Payload(服务端传递给客户端的数据)
payload: {
serverRendered: boolean
data: Record<string, any>
state: Record<string, any>
error?: Error | { url: string, statusCode: number, statusMessage: string }
_errors: Record<string, any>
[key: string]: any
}
// 是否在水合阶段
isHydrating: boolean
// 提供/注入(类似 Vue 的 provide/inject,但在 NuxtApp 层面)
provide: (name: string, value: any) => void
// 内部属性
_scope: EffectScope
_id: number
_processingPlugin: boolean
}面试关键点:NuxtApp 是每个 Nuxt 请求的核心上下文对象。在服务端,每个请求有独立的 NuxtApp 实例(通过 AsyncLocalStorage 或显式传递来隔离);在客户端,只有一个全局的 NuxtApp 实例。
5.3 NuxtSSRContext 数据结构
在服务端渲染时,每个请求都有一个 NuxtSSRContext 对象,它承载了 SSR 过程中的所有上下文信息:
interface NuxtSSRContext {
// 当前请求的 URL
url: string
// 请求的事件对象(h3 的 H3Event)
event: H3Event
// 请求的来源(IP、Host 等)
req: IncomingMessage // Node.js 环境
res: ServerResponse // Node.js 环境
// 渲染结果
noSSR: boolean // 是否跳过 SSR
error?: any // SSR 过程中的错误
// 渲染过程中的数据收集
payload: {
data: Record<string, any> // useAsyncData 的缓存
state: Record<string, any> // useState 的缓存
_errors: Record<string, any>
}
// 用于 <head> 标签管理(useHead)
head: HeadEntry[]
// 组件渲染追踪
renderedComponents?: Set<string>
// 岛屿组件相关
islands: Map<string, any>
// 渲染出的 HTML
renderResult?: {
html: string
}
// 缓存和去重
_asyncDataPromises: Record<string, Promise<any>>
// Teleports(Vue 的 <Teleport> 组件渲染结果)
teleports: Record<string, string>
}5.4 页面、布局与组件系统
页面(Pages)
pages/ 目录下的每个 .vue 文件对应一个路由。Nuxt 使用 vue-router 作为路由引擎,但增加了以下增强:
- 自动代码分割:每个页面组件自动使用
defineAsyncComponent包装,实现按需加载 - 路由元信息(Route Meta):通过
definePageMeta()宏定义页面级别的路由元数据 - 导航守卫:通过
definePageMeta()中的middleware属性指定页面级中间件
<!-- pages/index.vue -->
<script setup>
definePageMeta({
layout: 'default', // 使用哪个布局
middleware: ['auth'], // 使用哪些中间件
// 路由元信息
pageType: 'home',
requiresAuth: false,
// 过渡动画
pageTransition: { name: 'page', mode: 'out-in' },
layoutTransition: { name: 'layout', mode: 'out-in' },
// keep-alive
keepalive: true,
// 自定义滚动行为
scrollToTop: true
})
</script>注意:definePageMeta 是一个编译器宏(compiler macro),类似于 Vue 的 defineProps。它在构建时被提取出来作为路由元信息,不会出现在组件的 setup 代码中。Nuxt 通过一个 Vite/Rollup 插件在构建时将其提取。
布局(Layouts)
layouts/ 目录定义页面布局模板。布局本质上是带有 <slot /> 的 Vue 组件:
<!-- layouts/default.vue -->
<template>
<div>
<AppHeader />
<main>
<slot /> <!-- 页面内容在这里渲染 -->
</main>
<AppFooter />
</div>
</template>页面通过 definePageMeta({ layout: 'default' }) 选择布局。布局切换时会触发过渡动画。
嵌套布局的渲染链:
<NuxtLayout> ← 最外层布局
└── <NuxtPage> ← 页面组件
└── <NuxtLayout> ← 如果页面内部又有布局(嵌套路由场景)
└── <NuxtPage> ← 子路由页面5.5 Nuxt 内置组件
Nuxt 提供了几个核心的内置组件,它们是框架功能的入口点:
<NuxtPage> —— 路由出口,等价于 <RouterView> 但增加了 Nuxt 的增强:自动的页面过渡动画(pageTransition)、自动的 keepalive 支持、将 pageKey 作为 prop 传递,控制组件何时重新渲染。
<NuxtLayout> —— 布局容器,管理布局切换和布局过渡动画。
<NuxtLink> —— 增强的链接组件,基于 <RouterLink> 增加了:
- 预取(Prefetching):当链接进入视口时(通过 Intersection Observer),自动预取目标页面的 JS chunk 和数据
- 外部链接处理:自动识别外部链接并添加
rel="noopener" - active 状态:与 RouterLink 一样的 active/exact-active class 支持
<NuxtLink to="/about" prefetch>关于我们</NuxtLink>
<NuxtLink to="/products" :prefetch="false">产品</NuxtLink>
<NuxtLink to="https://example.com">外部链接</NuxtLink><NuxtLoadingIndicator> —— 页面导航进度条。
<NuxtErrorBoundary> —— 错误边界组件,捕获子组件中的错误。
<ClientOnly> —— 只在客户端渲染的内容(跳过 SSR)。
<ServerOnly> / <NuxtIsland> —— 岛屿架构相关组件(后面详述)。
<Suspense>(Vue 原生)—— Nuxt 的页面组件默认被 <Suspense> 包裹,支持异步 setup。
第六章 渲染系统深度剖析
这是面试中最常被问到的部分。
6.1 通用渲染(Universal Rendering)的完整流程
第一次请求(SSR):
① 浏览器发送 HTTP 请求 → https://example.com/products
② Nitro 服务器接收请求
- h3 路由匹配 → 进入 SSR 渲染处理器
- 检查 Route Rules:该路径是否配置了 SSR?
- 创建 NuxtSSRContext
③ Vue SSR 渲染
- 创建 Vue 应用实例(createSSRApp)
- 安装路由,设置当前 URL
- 安装所有 Nuxt 插件
- 执行页面组件的 setup()
→ useAsyncData() / useFetch() 被调用
→ 在服务端发起数据请求
→ 数据存入 NuxtSSRContext.payload.data
- Vue 渲染器将虚拟 DOM 渲染为 HTML 字符串
(使用 vue-bundle-renderer 的 renderToString)
- 收集渲染过程中触发的 <Head> 标签
- 收集 <Teleport> 内容
④ 组装完整 HTML
Nitro 将各部分拼接:
<!DOCTYPE html>
<html>
<head>
<!-- 来自 useHead() 的标签 -->
<title>Products</title>
<meta name="description" content="..." />
<link rel="stylesheet" href="/_nuxt/xxx.css" />
</head>
<body>
<div id="__nuxt">
<!-- SSR 渲染出的 HTML -->
<div class="products-page">
<h1>Products</h1>
<ul>
<li>Product 1 - $99</li>
<li>Product 2 - $199</li>
</ul>
</div>
</div>
<!-- Payload 注入脚本 -->
<script>
window.__NUXT__ = {
data: { "products-page": { data: [...], pending: false } },
state: { ... },
serverRendered: true,
config: { ... }
}
</script>
<script type="module" src="/_nuxt/entry.js"></script>
</body>
</html>
⑤ 返回 HTML 给浏览器
⑥ 浏览器渲染 HTML
- 用户立即看到页面内容(有内容但不可交互)
- 同时下载客户端 JS Bundle
⑦ 客户端 JS 执行
- 创建 Vue 应用实例(createSSRApp)
- 读取 window.__NUXT__ 中的 payload
- 使用 payload 中的数据初始化 useAsyncData 缓存
(避免客户端再次请求相同数据)
- 执行水合(Hydration):
Vue 将已有的 DOM 与虚拟 DOM 进行匹配,
添加事件监听器,激活响应式系统
⑧ 页面变为可交互状态(Interactive)6.2 水合(Hydration)详解
水合是 SSR 应用中最关键也最容易出问题的环节。
水合的本质:服务端渲染出了"静态"的 HTML(有结构但没有交互能力),水合是 Vue 在客户端"接管"这些 DOM 的过程:
- DOM 匹配:Vue 遍历服务端渲染的 DOM 树,与客户端虚拟 DOM 进行对比匹配
- 事件绑定:为匹配的 DOM 元素添加事件监听器(click、input 等)
- 响应式激活:将 Vue 的响应式系统绑定到 DOM 元素上
- 状态同步:从 payload 中恢复
useAsyncData、useState的数据
水合不匹配(Hydration Mismatch):
当服务端渲染的 HTML 结构与客户端首次渲染的虚拟 DOM 不一致时,就会发生水合不匹配。常见原因:
- 时间/日期相关:服务端和客户端的时区或时间不同
- 随机数/UUID:
Math.random()在服务端和客户端产生不同的值 - 浏览器 API:服务端没有
window、localStorage、document等对象 - 条件渲染:基于客户端状态(如媒体查询、用户偏好)的条件渲染
- HTML 嵌套不合法:如在
<p>中嵌套<div>(浏览器会自动修正,导致 DOM 结构与服务端不一致)
解决方案:
<!-- 方案1:使用 ClientOnly -->
<ClientOnly>
<ClientSideOnlyComponent />
</ClientOnly>
<!-- 方案2:使用 onMounted 延迟渲染 -->
<script setup>
const isClient = ref(false)
onMounted(() => { isClient.value = true })
</script>
<template>
<div v-if="isClient">{{ new Date().toLocaleString() }}</div>
<div v-else>Loading time...</div>
</template>
<!-- 方案3:使用 useHydration 的友好方式 -->
<script setup>
const nuxtApp = useNuxtApp()
// nuxtApp.isHydrating 为 true 时,说明正在水合
</script>6.3 客户端导航(Client-Side Navigation)
首次请求之后,用户点击 <NuxtLink> 进行的页面切换不再发起完整的 HTML 请求,而是客户端路由:
① 用户点击 <NuxtLink to="/about"> ② NuxtLink 拦截点击事件 - 调用 vue-router 的 push() - URL 变为 /about(History API) ③ Nuxt 客户端路由守卫触发 - 执行全局中间件(middleware/) - 执行页面级中间件(definePageMeta 中的 middleware) - 如果中间件调用了 navigateTo() 或 abortNavigation(),导航中止 ④ 加载目标页面的 JS chunk - 如果该 chunk 还没有加载,动态 import() - 这就是为什么 Nuxt 页面是自动代码分割的 ⑤ 执行目标页面的 setup() - useAsyncData() / useFetch() 被调用 - 如果 payload 中已有该页面的数据(通过 prefetch),直接使用 - 否则发起 API 请求获取数据 ⑥ 渲染新页面组件 - 触发页面过渡动画(如果有配置) - Vue 更新 DOM ⑦ 页面更新完成
6.4 Payload 传递机制
Payload 是 SSR 中服务端向客户端传递数据的核心机制。
服务端:
useAsyncData()和useFetch()获取的数据自动存入NuxtSSRContext.payload.datauseState()的值自动存入NuxtSSRContext.payload.state- 错误信息存入
NuxtSSRContext.payload._errors
序列化:Nuxt 使用自定义的序列化器(基于 destr 和 superjson 的思想)将 payload 序列化为 JavaScript 代码,嵌入到 HTML 的 <script> 标签中。支持的类型包括:Date、Map、Set、RegExp、URL、undefined、BigInt 等。
客户端:
- 客户端 JS 加载后,读取
window.__NUXT__(或通过内联模块方式注入) useAsyncData()检查 payload 中是否已有对应 key 的数据- 如果有,直接使用,不再发起网络请求(这就是 SSR 数据复用的实现)
- 如果没有(比如客户端导航到新页面),则发起新的请求
Payload 提取优化(Nuxt 3.x):在较新版本的 Nuxt 3 中,payload 不再通过 window.__NUXT__ 全局变量传递,而是通过 <script type="application/json"> 标签或 <script type="module"> 内联模块的方式传递,这允许浏览器以更高效的方式解析。
6.5 其他渲染模式
SPA 模式(Client-Only Rendering)
// nuxt.config.ts
export default defineNuxtConfig({
ssr: false // 全局关闭 SSR
})
// 或者通过 Route Rules 对特定路由关闭SPA 模式下,Nitro 服务器只返回一个空的 HTML 壳(包含 JS bundle 引用),所有渲染在客户端完成。与纯 Vue SPA 的区别是仍然可以使用 Nuxt 的文件路由、插件、模块等功能。
静态站点生成(SSG - Static Site Generation)
// nuxt.config.ts
export default defineNuxtConfig({
// 构建时使用 SSR 渲染每个路由,生成静态 HTML
// 运行时不需要 Node.js 服务器
})
// 执行 npx nuxi generatenuxi generate 会:构建应用 → 启动临时 Nitro 服务器 → 遍历所有路由 → 对每个路由执行 SSR 渲染 → 将 HTML 和静态资源输出到 .output/public/ 目录 → 部署时只需要一个静态文件服务器。
ISR(Incremental Static Regeneration)
routeRules: {
'/blog/**': { isr: 3600 } // 每小时重新生成
}页面在首次请求时静态生成并缓存。后续请求直接使用缓存。缓存过期后,下一个请求会触发后台重新生成,同时仍然返回旧的缓存(stale-while-revalidate 模式)。
岛屿架构(Island Architecture)
这是一种混合渲染策略:页面大部分是静态 HTML(不水合),只有标记为"岛屿"的组件会在客户端水合:
<template>
<div>
<!-- 这个组件在服务端渲染,不在客户端水合 -->
<StaticContent />
<!-- 这个组件在服务端渲染,并在客户端水合(可交互) -->
<NuxtIsland name="InteractiveChart" :props="{ data }" />
</div>
</template>好处:减少客户端 JS 体积和水合时间,提升首屏性能。适合页面中大部分内容是静态的、只有少部分是交互式的场景。
第七章 数据获取系统
7.1 useAsyncData —— 数据获取的基石
useAsyncData 是 Nuxt 3 数据获取的核心 composable。它解决了一个关键问题:如何让同一份数据获取代码在服务端和客户端都能正确运行,且数据能在两端之间同步。
// 签名
function useAsyncData<T>(
key: string, // 唯一标识符
handler: () => Promise<T>, // 数据获取函数
options?: AsyncDataOptions<T> // 配置项
): AsyncData<T>
function useAsyncData<T>(
handler: () => Promise<T>, // key 可以省略(自动基于文件路径生成)
options?: AsyncDataOptions<T>
): AsyncData<T>返回值数据结构:
interface AsyncData<T> {
data: Ref<T | null> // 数据(响应式引用)
pending: Ref<boolean> // 加载状态
status: Ref<'idle' | 'pending' | 'success' | 'error'>
error: Ref<Error | null> // 错误信息
refresh: () => Promise<void> // 手动刷新数据
execute: () => Promise<void> // 手动执行数据获取
clear: () => void // 清除数据
}配置项:
interface AsyncDataOptions<T> {
server?: boolean // 是否在服务端执行(默认 true)
lazy?: boolean // 是否懒加载(默认 false)
default?: () => T | Ref<T> // 默认值
transform?: (data: T) => T // 数据转换函数
pick?: string[] // 数据选取函数
watch?: Ref[] // 响应式值变化时自动重新获取
immediate?: boolean // 是否立即执行(默认 true)
deep?: boolean // 深度监听(默认 true)
dedupe?: 'cancel' | 'defer' // 去重策略
}内部工作原理:
useAsyncData(key, handler) 被调用
│
▼
检查 NuxtApp._asyncDataPromises[key]
是否已有相同 key 的进行中请求?
│
├── 有 → 复用已有的 Promise(去重)
│
└── 没有 →
│
▼
检查 NuxtApp.payload.data[key]
服务端是否已经获取了数据?(SSR payload)
│
├── 有 → 直接使用 payload 中的数据
│ 不调用 handler
│ data.value = payload.data[key]
│
└── 没有 →
│
▼
检查当前环境
│
├── 服务端(SSR)
│ → 调用 handler()
│ → 结果存入 payload.data[key]
│ → 会被序列化到 HTML 中
│
└── 客户端
→ 调用 handler()
→ 结果存入 data.value
→ 不进入 payload7.2 useFetch —— useAsyncData 的便捷封装
useFetch 是 useAsyncData + $fetch 的组合,用于最常见的"调用 API 获取数据"场景:
// 签名
function useFetch<T>(
url: string | Ref<string> | (() => string), // URL(可以是响应式的)
options?: UseFetchOptions<T>
): AsyncData<T>useFetch 的 options 扩展了 useAsyncData 的 options,额外支持:
interface UseFetchOptions<T> extends AsyncDataOptions<T> {
method?: string // 请求方法
query?: Record<string, any> // 查询参数
params?: Record<string, any> // URL 路径参数
body?: any // 请求体
headers?: Record<string, string> // 请求头
baseURL?: string // 基础 URL
onResponse?: (context: { response: Response }) => void
onResponseError?: (context: { response: Response }) => void
onRequest?: (context: { request: Request, options: any }) => void
onRequestError?: (context: { request: Request, options: any, error: Error }) => void
transform?: (data: any) => T
pick?: string[]
timeout?: number
}示例:
// 基本用法
const { data, pending, error, refresh } = await useFetch('/api/products')
// 带参数
const { data } = await useFetch('/api/products', {
method: 'POST',
body: { category: 'electronics' },
query: { page: 1, limit: 20 }
})
// URL 响应式(URL 变化时自动重新获取)
const id = ref(1)
const { data } = await useFetch(() => `/api/products/${id.value}`)
// watch 触发重新获取
const search = ref('')
const { data } = await useFetch('/api/search', {
query: { q: search },
watch: [search]
})
// transform + pick
const { data } = await useFetch('/api/users', {
transform: (response) => response.data,
pick: ['name', 'email']
})7.3 $fetch —— 底层 HTTP 客户端
$fetch 是 Nuxt 3 内置的 HTTP 客户端,基于 ofetch 库。它可以在服务端和客户端统一使用:
// 在服务端:
// - 如果请求的是本站 API(如 /api/xxx),直接调用 Nitro 处理函数
// 不发起真实的 HTTP 请求(零网络开销!)
// - 如果请求的是外部 URL,发起真实的 HTTP 请求
// 在客户端:
// - 总是发起真实的 HTTP 请求(fetch API)
// 用法
const data = await $fetch('/api/users', {
method: 'GET',
query: { page: 1 },
headers: { Authorization: 'Bearer xxx' }
})
// 注意:$fetch 不会自动处理 SSR 数据同步
// 如果需要 SSR 兼容,应该用 useFetch 或 useAsyncData + $fetch关键面试题:$fetch 和 useFetch 的区别是什么?
$fetch是一个纯函数式的 HTTP 请求工具,不关心 SSR/CSR 的数据同步useFetch=useAsyncData+$fetch,自动处理 SSR payload 传递、去重、响应式状态- 在
useAsyncData的 handler 中用$fetch是推荐做法 - 在事件处理函数(如按钮点击)中直接用
$fetch即可
7.4 服务端 API 中的直接调用(避免自调自)
这是一个重要的性能优化点。当 SSR 渲染时,页面组件中的 useFetch('/api/products') 实际上不需要发起真实的 HTTP 请求,因为请求者和被请求者在同一个进程中:
┌──────────────────────────────────────┐
│ Nitro 服务器进程 │
│ │
│ SSR 渲染器 │
│ │ │
│ ├── useFetch('/api/products') │
│ │ │ │
│ │ ▼ │
│ │ $fetch 检测到是本站 API │
│ │ │ │
│ │ ▼ │
│ │ 直接调用 API handler 函数 │
│ │ (不经过网络层,零开销) │
│ │ │ │
│ │ ▼ │
│ │ server/api/products.ts │
│ │ 的 default export 被直接调用 │
│ │ │
│ server/api/products.ts │
│ └── defineEventHandler(...) │
└──────────────────────────────────────┘这是通过 h3/Nitro 的 direct call 机制实现的:$fetch 在内部检查请求的 URL 是否匹配已注册的服务器处理器,如果匹配,直接调用处理函数而非发起 HTTP 请求。
7.5 useCookie —— SSR 安全的 Cookie 操作
useCookie 是 Nuxt 内置的 Cookie 读写 composable,解决了 SSR 中 Cookie 读写的核心难题:服务端没有 document.cookie。
// 签名
function useCookie<T = string | null>(
name: string,
options?: CookieOptions
): Ref<T | null>
interface CookieOptions {
path?: string // 默认 '/'
domain?: string
maxAge?: number // 秒
expires?: Date
httpOnly?: boolean
secure?: boolean
sameSite?: 'strict' | 'lax' | 'none' | boolean
default?: () => T // 默认值
watch?: boolean | 'shallow'
readonly?: boolean
}工作原理:
- 服务端:从
H3Event的请求头中解析Cookie字段,写入时通过Set-Cookie响应头发送给浏览器 - 客户端:使用
document.cookieAPI 读写 - 响应式:返回一个
Ref,当.value被修改时,自动将新的 Cookie 值写入
// 基本用法
const token = useCookie('auth-token')
token.value = 'new-jwt-token' // 自动写入 Cookie
// 带默认值和类型
const theme = useCookie<'light' | 'dark'>('theme', {
default: () => 'light',
maxAge: 60 * 60 * 24 * 365 // 1年
})
// 存储复杂数据(自动 JSON 序列化/反序列化)
const preferences = useCookie('user-prefs', {
default: () => ({ sidebar: true, compact: false })
})面试关键点:useCookie 在 SSR 中读写的是当前请求的 Cookie,而非浏览器中的 Cookie。这意味着在服务端修改 Cookie 后,同一请求中后续的读取能看到新值,但浏览器要等到收到 Set-Cookie 响应头后才会更新。
7.6 useRequestHeaders 与 useRequestFetch
这两个 composable 解决 SSR 中的一个常见问题:服务端发起的 API 请求不会自动携带客户端的请求头(如 Cookie、Authorization)。
// useRequestHeaders —— 获取当前 SSR 请求的请求头
const headers = useRequestHeaders() // 所有请求头
const cookies = useRequestHeaders(['cookie']) // 只要 cookie 头
const authHeaders = useRequestHeaders(['authorization']) // 只要 auth 头
// useRequestFetch —— 创建一个自动转发 SSR 请求头的 $fetch
const fetch = useRequestFetch()
const { data } = await useAsyncData('user', () =>
fetch('/api/auth/me') // 自动携带 SSR 请求的 Cookie
)为什么需要这个:在 SSR 渲染中,useFetch('/api/auth/me') 默认走 Nitro 的 direct call 机制(直接调用 handler 函数),此时 handler 中的 getRequestHeaders(event) 获取到的是内部调用的请求,而不是原始浏览器请求的 Cookie。useRequestFetch 会将原始请求的关键请求头(尤其是 Cookie)注入到内部调用中,确保认证信息不丢失。
推荐实践:
// composables/useAuth.ts
export const useAuth = () => {
const fetch = useRequestFetch()
const { data: user } = await useAsyncData('user', () =>
fetch('/api/auth/me').catch(() => null)
)
return { user, isLoggedIn: computed(() => !!user.value) }
}7.7 Nitro 存储层(unstorage)
Nitro 内置了 unstorage,提供了一个统一的键值存储抽象,可以对接多种后端:
// server/api/cache.ts
export default defineEventHandler(async (event) => {
const storage = useStorage()
await storage.setItem('cache:key', { data: 'value' })
const value = await storage.getItem('cache:key')
await storage.setItem('redis:user:123', userData)
return value
})// nuxt.config.ts —— 配置存储驱动
export default defineNuxtConfig({
nitro: {
storage: {
cache: { driver: 'memory' },
redis: {
driver: 'redis',
host: process.env.REDIS_HOST,
port: 6379
},
files: {
driver: 'fs',
base: './data'
}
}
}
})这在 Serverless 和 Edge 环境中特别有用,因为这些环境通常没有本地文件系统或持久化存储,unstorage 提供了一致的接口来对接 Cloudflare KV、Upstash Redis、AWS DynamoDB 等云存储服务。
第八章 状态管理
8.1 useState —— Nuxt 内置的跨端状态
useState 是 Nuxt 提供的最简单的跨端状态管理工具:
// 签名
function useState<T>(
key: string, // 唯一标识符
init?: () => T | Ref<T> // 初始值工厂函数
): Ref<T>工作原理:服务端创建响应式引用并存入 payload.state → SSR 完成后序列化到 HTML → 客户端水合时从 payload 恢复 → 后续客户端调用直接使用已有值。
// composables/useCounter.ts
export const useCounter = () => {
const count = useState('counter', () => 0)
const increment = () => count.value++
const decrement = () => count.value--
return { count, increment, decrement }
}8.2 Pinia 集成
Nuxt 3 官方推荐通过 @pinia/nuxt 模块集成 Pinia:
// stores/auth.ts
export const useAuthStore = defineStore('auth', () => {
const user = ref(null)
const isLoggedIn = computed(() => !!user.value)
async function login(credentials) {
user.value = await $fetch('/api/auth/login', {
method: 'POST',
body: credentials
})
}
function logout() {
user.value = null
navigateTo('/login')
}
return { user, isLoggedIn, login, logout }
})Pinia 与 SSR 的关系:Pinia 的 store 状态默认不会自动通过 SSR payload 传递。需要通过 Nuxt 的插件或 useState 来桥接。实际上,@pinia/nuxt 模块已经内置了这种 SSR 状态桥接,开发者不需要手动处理。
8.3 useAsyncData 缓存作为状态
在很多场景下,useAsyncData 的缓存本身就是一种状态管理方式。相同 key 的 useAsyncData 在多个组件中共享同一份数据:
// composables/useProducts.ts
export const useProducts = () => {
return useAsyncData('products', () => $fetch('/api/products'))
}
// 在组件 A 中
const { data: products } = await useProducts()
// 在组件 B 中
const { data: products } = await useProducts()
// 两个组件共享同一份数据,只发起一次请求第九章 插件系统
9.1 插件的定义与执行顺序
// plugins/myPlugin.ts
// 方式1:简单函数
export default defineNuxtPlugin((nuxtApp) => {
// nuxtApp 是运行时的 NuxtApp 实例
// nuxtApp.vueApp.use(someVuePlugin)
nuxtApp.provide('myHelper', () => { /* ... */ })
})
// 方式2:对象形式(更精细的控制)
export default defineNuxtPlugin({
name: 'my-plugin',
enforce: 'pre', // 执行时机:'pre' | 'default' | 'post'
dependsOn: ['other-plugin'],
async setup(nuxtApp) {
// 插件逻辑
},
hooks: {
'app:created'(vueApp) { /* ... */ },
'page:start'() { /* ... */ }
}
})
// 通过文件名控制环境:
// plugins/myPlugin.client.ts → 只在客户端执行
// plugins/myPlugin.server.ts → 只在服务端执行插件执行顺序:
enforce: 'pre'的插件最先执行enforce: 'default'(默认)的插件按文件名字母顺序执行enforce: 'post'的插件最后执行- 如果有
dependsOn,会等待依赖的插件完成后再执行
9.2 插件可以做什么
- 注册 Vue 插件:
nuxtApp.vueApp.use(pinia) - 注入全局方法/属性:
nuxtApp.provide('api', apiClient) - 注册全局组件:
nuxtApp.vueApp.component('MyComponent', MyComponent) - 注册全局指令:
nuxtApp.vueApp.directive('focus', { ... }) - 注册路由守卫:
nuxtApp.vueApp.use(router => { router.beforeEach(...) }) - 设置全局错误处理:
nuxtApp.vueApp.config.errorHandler = (...) - 注册 NuxtApp 钩子:
nuxtApp.hook('page:finish', () => { ... })
第十章 中间件系统
10.1 路由中间件
路由中间件在每次页面导航时执行,用于权限验证、数据预加载、日志等:
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
const authStore = useAuthStore()
if (!authStore.isLoggedIn && to.meta.requiresAuth) {
return navigateTo('/login')
}
// 不返回值 = 继续导航
// 返回 navigateTo('/path') = 重定向
// 返回 abortNavigation() = 中止导航
// 返回 abortNavigation(error) = 中止并设置错误
})中间件分类:
- 全局中间件:
middleware/目录下文件名以.global结尾的文件,每次导航都执行 - 命名中间件:普通文件名的中间件,需要在
definePageMeta({ middleware: ['auth'] })中引用 - 匿名中间件:直接在
definePageMeta中定义内联中间件
中间件执行顺序:全局中间件(按文件名字母序) → 命名中间件(按 definePageMeta 中数组的顺序)
执行环境细节:路由中间件同时在服务端和客户端执行。在 SSR 首次请求时,Nuxt 会在服务端执行路由中间件。在后续客户端导航时,中间件在客户端执行。这意味着中间件代码应该避免直接使用浏览器专有 API,或通过 import.meta.client / import.meta.server 做环境判断。
10.2 服务端中间件(Server Middleware)
// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
// 对每个 API 请求和 SSR 请求都会执行
const token = getHeader(event, 'Authorization')
if (token) {
event.context.user = await verifyToken(token)
}
// 不返回值 = 继续处理
// 返回值 = 直接响应,不再执行后续处理
})第十一章 模块系统
11.1 模块的概念与作用
模块是 Nuxt 的扩展机制。一个模块可以在构建时修改 Nuxt 的配置、注册组件、添加插件、注入服务器处理器等。Nuxt 生态的绝大部分功能(Tailwind CSS、Pinia、Content、Image、Auth 等)都是通过模块提供的。
11.2 模块的定义
// modules/myModule.ts
import { defineNuxtModule, addPlugin, addComponent, createResolver } from '@nuxt/kit'
export default defineNuxtModule({
meta: {
name: 'my-module',
configKey: 'myModule',
compatibility: { nuxt: '>=3.0.0' }
},
defaults: {
apiKey: '',
enableFeature: true
},
async setup(options, nuxt) {
const resolver = createResolver(import.meta.url)
// 1. 注册插件
addPlugin(resolver.resolve('./runtime/plugin'))
// 2. 注册组件
addComponent({
name: 'MyComponent',
filePath: resolver.resolve('./runtime/MyComponent.vue')
})
// 3. 注册 composable(使其可被自动导入)
nuxt.hook('imports:dirs', (dirs) => {
dirs.push(resolver.resolve('./runtime/composables'))
})
// 4. 注册服务器 API
addServerHandler({
route: '/api/my-module/data',
handler: resolver.resolve('./runtime/server/api/data')
})
// 5. 修改 Nuxt 配置
nuxt.options.css.push('my-module/dist/styles.css')
// 6. 修改 Vite 配置
nuxt.hook('vite:extendConfig', (config) => {
config.resolve.alias['#my-module'] = resolver.resolve('./runtime')
})
// 7. 注册 Nitro 插件
nuxt.hook('nitro:config', (nitroConfig) => {
nitroConfig.plugins = nitroConfig.plugins || []
nitroConfig.plugins.push(resolver.resolve('./runtime/server/plugin'))
})
}
})11.3 模块的生命周期钩子
Nuxt 在构建过程中会触发一系列钩子,模块可以监听这些钩子:
modules:before ← 模块安装前 modules:done ← 所有模块安装完成 app:resolve ← Vue 应用配置解析完成 app:templates ← 虚拟文件模板生成前 app:templatesGenerated ← 虚拟文件生成完成 build:before ← 构建开始前 build:done ← 构建完成 pages:extend ← 路由表生成后(可添加/修改路由) components:extend ← 组件列表生成后 imports:dirs ← 自动导入目录收集 imports:extend ← 自动导入列表生成后 nitro:config ← Nitro 配置生成后 nitro:init ← Nitro 实例创建后 vite:extendConfig ← Vite 配置生成后 vite:serverCreated ← Vite 开发服务器创建后
11.4 @nuxt/kit 核心 API 速查
defineNuxtModule({ meta, defaults, setup }) // 定义模块
addPlugin({ src, mode }) // 添加插件
addComponent({ name, filePath }) // 添加组件
addServerHandler({ route, handler, method, middleware }) // 添加服务器处理器
addServerPlugin({ src }) // 添加服务器插件
addRouteMiddleware({ name, path, global }) // 添加路由中间件
extendPages((pages) => { pages.push(...) }) // 修改页面路由
extendViteConfig((config) => { ... }) // 修改 Vite 配置
extendWebpackConfig((config) => { ... }) // 修改 Webpack 配置
addVitePlugin(plugin) // 安装 Vite 插件
createResolver(import.meta.url) // 创建路径解析器
useNuxt() // 获取 Nuxt 实例
useLogger('my-module') // 创建日志第十二章 Nuxt 钩子系统(Hooks)
12.1 NuxtApp 钩子(运行时)
这些钩子在运行时触发,可在插件或组件中监听:
// 应用生命周期
app:beforeMount // Vue 应用挂载前
app:mounted // Vue 应用挂载完成
app:error // 应用发生错误
app:suspense:resolve // Suspense 解析完成
// 页面导航
page:start // 页面导航开始
page:finish // 页面导航完成
page:loading:start // 页面加载开始
page:loading:end // 页面加载结束
page:transition:finish // 页面过渡动画完成
// 数据获取
asyncData:start // useAsyncData 开始
asyncData:end // useAsyncData 结束
// 渲染
app:rendered // SSR 渲染完成(仅服务端)
app:chunkError // 加载 JS chunk 失败
// 使用方式
const nuxtApp = useNuxtApp()
nuxtApp.hook('page:finish', () => {
console.log('Page navigation finished')
})12.2 Nuxt 钩子(构建时)
这些钩子在构建时触发,可在模块中监听(第十一章已列出)。
12.3 Nitro 钩子(服务器运行时)
// 请求生命周期
request // 收到请求
beforeResponse // 响应发送前
afterResponse // 响应发送后
error // 请求处理出错
// 渲染
render:html // HTML 渲染后(可修改 HTML)
render:response // 响应准备发送前
// 使用方式(在 Nitro 插件中)
// server/plugins/renderHook.ts
export default defineStritroPlugin((nitroApp) => {
nitroApp.hooks.hook('render:html', (html, { event }) => {
// html 是一个对象,包含 head、bodyAppend 等数组
html.head.push('<link rel="preconnect" href="https://fonts.googleapis.com" />')
})
})第十三章 目录结构与文件约定详解
13.1 完整目录结构
my-nuxt-app/
├── .nuxt/ # 自动生成的虚拟文件(不要手动修改,加入 .gitignore)
├── .output/ # 生产构建产物
├── app.vue # 应用根组件(入口组件)
├── app.config.ts # 应用配置(可在运行时通过 payload 传递)
├── error.vue # 全局错误页面
├── nuxt.config.ts # Nuxt 配置(构建时)
├── tsconfig.json # TypeScript 配置
│
├── assets/ # 静态资源(会被构建工具处理)
│ ├── css/
│ ├── images/
│ └── fonts/
│
├── public/ # 公共资源(直接复制到输出目录)
│ ├── favicon.ico
│ └── robots.txt
│
├── components/ # Vue 组件(自动注册)
│ ├── AppHeader.vue # → <AppHeader />
│ ├── AppFooter.vue # → <AppFooter />
│ ├── ui/
│ │ ├── Button.vue # → <UiButton />
│ │ └── Input.vue # → <UiInput />
│ └── base/
│ └── Card.vue # → <BaseCard />
│
├── composables/ # 组合式函数(自动导入)
│ ├── useAuth.ts
│ ├── useProducts.ts
│ └── useCounter.ts
│
├── utils/ # 工具函数(自动导入)
│ ├── formatters.ts
│ └── validators.ts
│
├── layouts/ # 页面布局
│ ├── default.vue
│ ├── auth.vue
│ └── blank.vue
│
├── pages/ # 路由页面
│ ├── index.vue # /
│ ├── about.vue # /about
│ ├── products/
│ │ ├── index.vue # /products
│ │ ├── [id].vue # /products/:id(动态路由)
│ │ └── [...slug].vue # /products/*(通配路由)
│ └── users/
│ ├── index.vue # /users
│ └── [id]/
│ ├── index.vue # /users/:id
│ └── settings.vue # /users/:id/settings(嵌套路由)
│
├── middleware/ # 路由中间件
│ ├── auth.global.ts # 全局中间件
│ └── admin.ts # 命名中间件
│
├── plugins/ # Nuxt 插件
│ ├── vue-plugins.ts
│ ├── analytics.client.ts # 只在客户端执行
│ └── i18n.ts
│
├── server/ # 服务器代码(Nitro)
│ ├── api/ # API 路由
│ ├── routes/ # 自定义路由
│ ├── middleware/ # 服务器中间件
│ ├── plugins/ # Nitro 插件
│ └── utils/ # 服务器工具函数(自动导入)
│
├── stores/ # Pinia stores
│ └── auth.ts
│
└── types/ # TypeScript 类型定义
└── index.d.ts13.2 动态路由命名约定
| 文件名 | 路由路径 | 说明 |
|---|---|---|
[id].vue | /:id | 单个动态参数 |
[[id]].vue | /:id? | 可选动态参数 |
[...slug].vue | /* | 通配(catch-all)路由 |
[[...slug]].vue | /* (可选) | 可选通配路由 |
[id]-[name].vue | /:id-:name | 多个参数混合文本 |
13.3 特殊文件说明
app.vue
应用根组件。如果存在 pages/ 目录,app.vue 中通常需要包含 <NuxtPage />。
<!-- app.vue -->
<template>
<div>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</div>
</template>error.vue
全局错误页面。当应用发生未捕获的错误时显示。
<!-- error.vue -->
<script setup>
const props = defineProps(['error'])
const handleError = () => clearError({ redirect: '/' })
</script>
<template>
<div>
<h1>{{ error.statusCode }}</h1>
<p>{{ error.message }}</p>
<button @click="handleError">返回首页</button>
</div>
</template>app.config.ts
应用级配置,可在运行时通过 useAppConfig() 访问。与 nuxt.config.ts 的区别:nuxt.config.ts 是构建时配置(修改需要重新构建),app.config.ts 理论上可以在运行时更新。
// app.config.ts
export default defineAppConfig({
theme: {
primaryColor: '#3b82f6',
dark: false
},
siteName: 'My App'
})
// 在组件中使用
const appConfig = useAppConfig()
console.log(appConfig.theme.primaryColor)第十四章 性能优化策略
14.1 自动代码分割
Nuxt 自动对以下内容进行代码分割:
- 页面组件:每个
pages/下的页面自动成为独立的 chunk - 异步组件:使用
defineAsyncComponent加载的组件 - 动态导入:
import()语句自动分割
14.2 预取策略
<NuxtLink> 组件内置了智能预取:
// NuxtLink 的预取逻辑(简化)
class NuxtLinkPrefetch {
// 1. 使用 IntersectionObserver 监听链接是否进入视口
observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.prefetchRoute(entry.target.href)
}
})
})
// 2. 预取目标页面的 JS chunk
async prefetchRoute(url) {
// 动态 import() 页面组件
// 浏览器会在后台下载但不执行
}
// 3. 可选:预取页面的数据
async prefetchData(url) {
// 提前调用 API 获取数据
}
}14.3 图片优化
通过 @nuxt/image 模块:
<NuxtImg
src="/images/hero.jpg"
width="800"
height="400"
format="webp"
quality="80"
loading="lazy"
placeholder
/>自动处理:格式转换(WebP/AVIF)、尺寸优化、懒加载、响应式图片(srcset)、低质量占位图。
14.4 字体优化
Nuxt 3 内置了 @nuxt/fonts 模块,自动处理:字体文件自托管、预加载关键字体、字体子集化(只包含页面使用的字符)、font-display: swap 防止 FOIT。
14.5 SEO 优化(useHead)
useHead({
title: 'My Page Title',
meta: [
{ name: 'description', content: 'Page description for SEO' },
{ property: 'og:title', content: 'My Page Title' },
{ property: 'og:image', content: 'https://example.com/image.jpg' }
],
link: [
{ rel: 'canonical', href: 'https://example.com/page' }
],
script: [
{ src: 'https://analytics.example.com/script.js', defer: true }
]
})
// 或者使用 useSeoMeta(更便捷的 SEO 元信息)
useSeoMeta({
title: 'My Page',
description: 'Page description',
ogTitle: 'My Page',
ogDescription: 'Page description',
ogImage: 'https://example.com/image.jpg',
twitterCard: 'summary_large_image'
})useHead 底层使用 @unhead/vue,它会在 SSR 时将所有 head 标签序列化到 HTML 的 <head> 中,在客户端通过 DOM 操作动态更新。
14.6 渲染优化
- 路由级别的渲染策略:通过 Route Rules 对不同路由使用不同策略
- 组件懒加载:
<LazyMyComponent />语法糖 - Suspense 边界:页面组件天然支持异步 setup
- Islands:减少客户端 JS 体积
- Payload 提取:新版 Nuxt 支持将 payload 从 HTML 中提取为独立文件
第十五章 部署架构
15.1 部署模式对比
| 部署模式 | 运行环境要求 | 适用场景 | 构建命令 |
|---|---|---|---|
| Node.js Server | Node.js 进程 | 需要 SSR、API 路由 | npx nuxi build + node .output/server/index.mjs |
| Static Hosting | 任意静态文件服务器 | 纯静态网站、博客 | npx nuxi generate |
| Serverless | AWS Lambda 等 | 弹性伸缩、低流量 | npx nuxi build(preset: aws-lambda) |
| Edge | Cloudflare Workers 等 | 低延迟、全球分发 | npx nuxi build(preset: cloudflare) |
| Docker | Docker 运行时 | 容器化部署 | npx nuxi build + Dockerfile |
15.2 Node.js 部署
# 构建
npx nuxi build
# 启动(生产)
PORT=3000 HOST=0.0.0.0 node .output/server/index.mjs
# 或使用 PM2
pm2 start .output/server/index.mjs --name my-nuxt-app15.3 Docker 部署
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/.output .output
EXPOSE 3000
CMD ["node", ".output/server/index.mjs"]15.4 环境变量
Nuxt 3 有两种环境变量:构建时环境变量(通过 nuxt.config.ts 中的 env 或 .env 文件)和运行时环境变量(通过 runtimeConfig 配置):
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
// 仅在服务端可用
apiSecret: process.env.API_SECRET,
dbUrl: process.env.DATABASE_URL,
// 在客户端和服务端都可用
public: {
apiBase: process.env.NUXT_PUBLIC_API_BASE || 'https://api.example.com',
appName: 'My Nuxt App'
}
}
})
// 在代码中使用
const config = useRuntimeConfig()
// 服务端:可以访问 config.apiSecret 和 config.public.apiBase
// 客户端:只能访问 config.public.apiBase环境变量覆盖规则:运行时配置可以通过环境变量覆盖,命名规则为 NUXT_ 前缀 + 大写 + 下划线分隔。例如 runtimeConfig.apiSecret → NUXT_API_SECRET,runtimeConfig.public.apiBase → NUXT_PUBLIC_API_BASE。
第十六章 测试策略
16.1 测试工具链
Nuxt 3 官方提供 @nuxt/test-utils,集成了 Vitest:
// vitest.config.ts
import { defineVitestConfig } from '@nuxt/test-utils/config'
export default defineVitestConfig({
// 自动设置 Nuxt 测试环境
})16.2 测试类型
组件单元测试
// components/__tests__/Button.spec.ts
import { mountSuspended } from '@nuxt/test-utils/runtime'
import Button from '~/components/ui/Button.vue'
test('Button renders correctly', async () => {
const component = await mountSuspended(Button, {
props: { label: 'Click me' }
})
expect(component.text()).toContain('Click me')
})mountSuspended 是增强版 mount,会等待所有异步 setup(useAsyncData 等)完成后再返回。
Composable 测试
import { mockNuxtImport } from '@nuxt/test-utils/runtime'
// Mock 自动导入的 composable
mockNuxtImport('useFetch', () => {
return () => ({
data: ref({ items: [] }),
pending: ref(false),
error: ref(null)
})
})API 路由测试
import { $fetch, setup } from '@nuxt/test-utils/e2e'
await setup({ server: true })
test('GET /api/products', async () => {
const data = await $fetch('/api/products')
expect(data).toHaveProperty('items')
})E2E 测试(结合 Playwright)
import { expect, test } from '@playwright/test'
import { setup, $fetch } from '@nuxt/test-utils/e2e'
await setup({ server: true })
test('home page renders', async ({ page }) => {
await page.goto('/')
await expect(page.locator('h1')).toHaveText('Welcome')
})第十七章 高频面试题与深度解答
Q1: Nuxt 和纯 Vue SPA 有什么本质区别?
答:本质区别在于 Nuxt 在 Vue 之上增加了服务端层和构建编排层:
- 渲染能力:Nuxt 支持 SSR、SSG、ISR、混合渲染,而 Vue SPA 只有客户端渲染
- 路由系统:Nuxt 基于文件系统自动生成路由
- 全栈能力:Nuxt 内置了 Nitro 服务器引擎,可以直接写 API 端点
- 构建优化:Nuxt 自动配置代码分割、预取、资源优化
- 数据获取协调:
useAsyncData/useFetch自动处理 SSR-CSR 数据同步 - 模块生态:通过模块系统快速集成 Tailwind、Pinia、i18n 等
Q2: Nuxt 3 为什么选择 Nitro 作为服务器引擎?
答:Nuxt 2 的服务器紧耦合于 Node.js + Connect/Express,无法部署到 Edge Runtime。Nitro 基于 h3(使用 Web Standards API),通过 Rollup 构建和 tree-shaking,通过 preset 系统适配不同平台。同一个 Nuxt 应用可以用不同 preset 部署到不同平台,代码不需要任何修改。
Q3: SSR 中数据是如何从服务端传递到客户端的?
答:通过 Payload 机制。SSR 渲染时 useAsyncData/useFetch 的数据存入 NuxtSSRContext.payload.data,useState 存入 payload.state。渲染完成后序列化为 JSON 嵌入 HTML 的 <script> 标签中。客户端水合时读取 payload 恢复数据,避免重复请求。
Q4: 什么是水合不匹配?如何避免?
答:水合不匹配发生在服务端渲染的 HTML 与客户端虚拟 DOM 不一致时。常见原因:浏览器 API(window/document)、时间/随机数、HTML 非法嵌套。解决方案:使用 <ClientOnly> 包裹、import.meta.client 条件判断、确保 HTML 语义正确。
Q5: useFetch 和 useAsyncData 应该如何选择?
答:useFetch 是 useAsyncData + $fetch 的便捷封装。调用本站 API 用 useFetch(最简洁);调用外部 API 或需要自定义逻辑用 useAsyncData + $fetch;非 HTTP 请求用 useAsyncData。两者在 SSR 数据同步方面行为一致。
Q6: Nuxt 的自动导入是怎么实现的?
答:自动导入是构建时的代码转换。Nuxt 使用 unimport 扫描所有可导入来源,生成 import map,通过 Vite 插件进行 AST 分析,自动插入缺失的 import 语句。最终产物是普通的 ES module import,零运行时开销。
Q7: Nuxt 模块和 Vue 插件有什么区别?
答:Nuxt 模块工作在构建时,可修改配置、注册组件、添加服务器处理器等;Vue 插件工作在运行时,通过 app.use() 注册。一个 Nuxt 模块通常会注册 Vue 插件,但职责更广。
Q8: Nuxt 中间件和服务端中间件有什么不同?
答:路由中间件(middleware/)在客户端导航时执行,用于页面级权限验证;服务端中间件(server/middleware/)在 Nitro 服务器上执行,对每个 HTTP 请求都执行,用于 API 认证、CORS 等。
Q9: 如何在 Nuxt 中处理认证?
答:典型架构:登录 → 获取 token → HttpOnly Cookie 存储(最安全)→ 路由中间件保护客户端路由 → server middleware 验证服务端请求 → SSR 时使用 useRequestFetch() 转发 Cookie。
// composables/useAuth.ts
export const useAuth = () => {
const user = useState('user', () => null)
const fetch = useRequestFetch() // 自动转发 SSR Cookie
const { data } = await useAsyncData('user', () =>
fetch('/api/auth/me').catch(() => null)
)
user.value = data.value
return { user, isLoggedIn: computed(() => !!user.value) }
}Q10: Nuxt 3 如何处理错误?
答:多层错误处理:<NuxtErrorBoundary>(组件级)、app:error 钩子(应用级)、error.vue(全局错误页)、createError(API 错误)、useFetch 的 onResponseError(请求错误)。
// 抛出错误
throw createError({
statusCode: 404,
statusMessage: 'Page Not Found',
fatal: true
})
// 全局错误处理插件
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('app:error', (error) => {
console.error('Global error:', error)
})
})Q11-Q16: 更多面试要点速览
- Q11 Route Rules:取代了 Nuxt 2 中分散的路由级配置,支持 SSR/SPA/ISR/SWR/重定向等,通配符匹配,同一应用可混合使用不同策略。
- Q12 NuxtLink 预取:使用 IntersectionObserver + 动态 import(),是 JavaScript 级别的预取,不同于浏览器级
<link rel="prefetch">。 - Q13 渲染模式选择:SSR 适合动态内容+SEO;SSG 适合静态内容;ISR 介于两者之间;SPA 适合不需 SEO 的管理后台。推荐 Route Rules 混合使用。
- Q14 definePageMeta 为何是编译器宏:路由元信息需在构建时提取(而非运行时),确保导航守卫在不加载组件代码时就能访问元信息。
- Q15 nuxt.config.ts vs app.config.ts:前者是构建时工程配置(修改需重新构建),后者是应用级参数(可在运行时通过模块注入或 Nitro 插件覆盖)。
- Q16 SSR Cookie 转发:使用
useRequestFetch()创建自动转发原始请求头的 $fetch 实例,确保认证信息不丢失。
第十八章 数据流全景总结
18.1 完整数据流示意图
┌──────────────────┐
│ 浏览器/客户端 │
└────────┬─────────┘
│
HTTP 请求(首次请求 / API 调用)
│
┌────────▼─────────┐
│ Nitro Server │
│ (h3 路由匹配) │
└───┬──────────┬───┘
│ │
┌────────────▼──┐ ┌────▼────────────┐
│ SSR 渲染路径 │ │ API 路由路径 │
│ │ │ │
│ 创建 Vue App │ │ 执行 Handler │
│ 执行 setup() │ │ defineEventHandler│
│ useFetch → │ │ │
│ 直接调用 │ │ 返回 JSON │
│ API Handler │ │ │
│ (无网络开销) │ └────────┬────────┘
│ │ │
│ 渲染 HTML │ │
│ 序列化 payload │ │
└───────┬───────┘ │
│ │
HTML + Payload JSON 响应
│ │
┌──────▼──────────────────▼──┐
│ 浏览器接收 │
└──────┬──────────────────┬──┘
│ │
渲染 HTML(立即可见) │
下载 JS Bundle │
│ │
┌──────▼──────┐ │
│ 水合阶段 │ │
│ 读取 payload │ │
│ 恢复状态 │ │
│ 绑定事件 │ │
│ 激活响应式 │ │
└──────┬──────┘ │
│ │
┌──────▼──────┐ │
│ 可交互状态 │ │
│ 后续导航: │ │
│ 客户端路由 │ │
│ + API 请求 │───────────┘
└─────────────┘18.2 核心数据流向一句话总结
服务端到客户端:数据通过 SSR 渲染时获取 → 存入 payload → 序列化到 HTML → 客户端水合时读取 payload → 恢复为响应式状态。
客户端到服务端:用户操作 → 触发事件 → $fetch/useFetch 发起 HTTP 请求 → Nitro 路由匹配 → 执行 API handler → 返回 JSON → 更新客户端状态。
服务端内部快捷路径:SSR 渲染时 useFetch('/api/xxx') → Nitro 检测到是本站 API → 直接调用 handler 函数(不经过网络层)→ 零延迟获取数据。
第十九章 高级话题
19.1 自定义渲染器
Nuxt 允许自定义 HTML 渲染过程,通过 Nitro 的 render:html 钩子:
// server/plugins/customRenderer.ts
export default defineStritroPlugin((nitroApp) => {
nitroApp.hooks.hook('render:html', (html, { event }) => {
// html 结构:
// {
// htmlAttrs: string[], // <html> 的属性
// head: string[], // <head> 内容
// bodyAttrs: string[], // <body> 的属性
// bodyPrepend: string[], // <body> 开头注入
// body: string[], // <body> 主要内容
// bodyAppend: string[] // <body> 结尾注入
// }
html.head.push('<meta name="custom" content="value" />')
html.bodyAppend.push('<script>console.log("injected")</script>')
})
})19.2 自定义 Nitro Preset
如果要部署到不被官方支持的平台,可以创建自定义 preset:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'custom-preset',
rollupConfig: {
// 自定义 Rollup 配置
}
}
})19.3 多层应用(Multi-App / Layers)
Nuxt 3 支持将多个 Nuxt 应用组合为一个,称为 Layers:
// nuxt.config.ts
export default defineNuxtConfig({
extends: [
'./base-app', // 本地路径
'my-shared-layer', // npm 包
'github:org/repo' // GitHub 仓库
]
})Layer 可以提供:页面、组件、composables、插件、服务器处理器、配置等。所有 layer 的功能会被合并到主应用中。适用于微前端、共享基础应用、团队间代码共享。
19.4 Virtual Files(虚拟文件系统)
Nuxt 的虚拟文件系统(VFS)是构建时的关键概念。.nuxt/ 目录下的文件在开发模式下通过 Vite 的虚拟模块机制在内存中提供:
// 在代码中可以使用 # 前缀引用虚拟文件
import { useAppConfig } from '#app'
import { useRuntimeConfig } from '#imports'这些 #app、#imports 等别名指向的就是 VFS 中的虚拟模块。
19.5 DevTools 集成
Nuxt DevTools 是官方提供的开发调试工具,可以:查看路由和导航历史、检查 payload 数据、查看模块和插件列表、监控服务器 API 请求、分析组件依赖关系、查看 Vite 构建信息。
附录:关键术语速查表
| 术语 | 说明 |
|---|---|
| Nitro | Nuxt 3 的服务器引擎,与平台无关 |
| h3 | Nitro 底层的 HTTP 框架 |
| NuxtApp | 运行时应用实例(每个请求一个,客户端全局一个) |
| NuxtSSRContext | SSR 时的请求上下文 |
| Payload | 服务端传递给客户端的序列化数据 |
| Hydration | 客户端接管服务端渲染的 DOM 的过程 |
| Route Rules | 路由级别的渲染策略配置 |
| ISR | Incremental Static Regeneration,增量静态再生成 |
| SWR | Stale-While-Revalidate,过期缓存策略 |
| Islands | 岛屿架构,部分组件客户端水合 |
| useAsyncData | 核心数据获取 composable |
| useFetch | useAsyncData + $fetch 的便捷封装 |
| $fetch | 底层 HTTP 客户端(ofetch) |
| navigateTo | 编程式导航函数 |
| definePageMeta | 页面元信息编译器宏 |
| defineNuxtModule | 模块定义函数 |
| defineNuxtPlugin | 插件定义函数 |
| defineEventHandler | 服务器 API 处理函数定义 |
| createError | 创建结构化错误 |
| useRuntimeConfig | 访问运行时配置 |
| useAppConfig | 访问应用配置 |
| useState | Nuxt 内置的跨端状态管理 |
| useHead | 管理 HTML head 标签 |
| useCookie | SSR 安全的 Cookie 读写 composable |
| useRequestFetch | 自动转发 SSR 原始请求头的 $fetch 实例 |
| useRequestHeaders | 获取 SSR 原始请求的请求头 |
| unstorage | Nitro 内置的统一键值存储抽象 |
| NuxtLink | 增强的链接组件(预取 + 路由) |
| NuxtPage | 路由出口组件 |
| NuxtLayout | 布局容器组件 |
| preset | Nitro 的部署平台预设 |
| unimport | 自动导入的底层库 |
| VFS | Virtual File System,虚拟文件系统 |
| jiti | TypeScript 配置文件加载器 |
本文档覆盖了 Nuxt 3 从架构设计到运行时数据流的核心知识。掌握这些内容后,面试中关于 Nuxt 的问题——无论是架构设计理念、SSR 原理、数据获取机制、模块系统、性能优化还是部署策略——都可以从容应对。