一、为什么选 Nuxt 3:把渲染策略收进同一套约定
Nuxt 3 是构建在 Vue 3 之上的全栈 Web 框架,它把服务端渲染(SSR)、静态生成(SSG)与边缘渲染统一到约定式目录里。对需要 SEO 与首屏速度的 Vue 团队来说,Nuxt 3 让你用最少配置拿到生产级渲染能力:约定式文件路由、自动代码分割、内置 Nitro 服务器引擎,以及开箱即用的数据获取与元信息管理能力。本文用可运行示例讲清从起步到上线的关键路径。
在元框架选型上,Nuxt 3 与 Next.js 的 React Server Components、Astro 的内容优先架构、SvelteKit 的全栈路由 思路相通,但底层仍是 Vue 的开发体验。下面的对照表帮你快速判断它是否适合当前项目。
对已经用 Vue 3 写单页应用(SPA)的团队,迁移到 Nuxt 3 的边际成本很低:组件写法几乎不变,只是把路由、数据获取与渲染策略交还给框架统一处理。你失去的是”完全自定义构建链路”的自由度,换来的则是开箱即用的 SSR、自动代码分割、内建服务器与部署预设。对于电商详情页、内容聚合中台、需要被搜索引擎抓取的活动页这类场景,这个交换非常划算;而纯后台管理系统若对首屏与收录不敏感,继续用 SPA 也完全合理,不必为了追新而强行上 SSR。
| 维度 | Nuxt 3 | Astro | SvelteKit |
|---|---|---|---|
| 底层框架 | Vue 3 | 多框架 | Svelte |
| 默认渲染 | SSR/SSG | 静态+群岛 | SSR |
| 服务器引擎 | Nitro(边缘友好) | Node/SSR 适配器 | Adapter |
| 适合场景 | Vue 全栈/电商/中台 | 内容站/博客 | Svelte 全栈应用 |
二、快速起步:五分钟跑起一个 Nuxt 3 项目
用官方脚手架 nuxi 初始化即可,无需手写 Webpack/Vite 配置;Nuxt 3 默认基于 Vite,与 现代前端构建工具演进 一脉相承。
# 初始化项目
npx nuxi@latest init my-nuxt-app
cd my-nuxt-app
npm install
npm run dev # 默认 http://localhost:3000
# 关键脚本(package.json 节选)
{
"scripts": {
"dev": "nuxi dev",
"build": "nuxi build",
"generate": "nuxi generate",
"preview": "nuxi preview"
}
}
三、文件路由与布局:目录即路由
Nuxt 3 的 pages/ 目录会自动生成路由,文件结构直接映射 URL。动态参数用方括号目录,嵌套布局用 layouts/。
// pages/index.vue → /
<template>
<h1>首页</h1>
</template>
// pages/posts/[id].vue → /posts/123
<script setup>
const route = useRoute()
const id = route.params.id
</script>
// layouts/default.vue 全局布局
<template>
<div>
<NavBar />
<slot />
</div>
</template>
四、数据获取:useFetch 与 useAsyncData
客户端与服务端共享同一套数据获取 API。Nuxt 会在服务端预取数据并序列化到 HTML,避免水合后再发一次请求,对首屏与 SEO 都更友好。更多首屏优化思路可参考 前端性能优化实战。
// 自动按组件名去重、跨请求复用
const { data: posts } = await useFetch('/api/posts')
// 对已有异步函数做封装,拿到更细的状态
const { data, pending, error, refresh } = await useAsyncData(
'user-1',
() => $fetch('/api/user/1')
)
// 客户端导航时带缓存,避免重复拉取
const { data: detail } = await useFetch(
() => `/api/posts/${route.params.id}`,
{ watch: [() => route.params.id] }
)
生产项目里,数据获取最易踩的坑是”客户端切换路由时重复拉取”和”缓存键冲突”。useAsyncData 的第一个参数是缓存键,相同 key 在多次调用间会复用结果;配合 watch 监听路由参数,则能在参数变化时自动刷新,既保证数据新鲜又避免无谓请求。若接口返回较大列表且变化不频繁,可再用 refresh 手动触发刷新,或结合 lazy: true 让首屏不被阻塞。记住一条经验:凡是服务端能预取的,就不要留到客户端水合后再取,这是 Nuxt 3 首屏与 SEO 优势的根本来源。
五、服务端 API:Nitro 的 server 路由
Nuxt 3 内置 Nitro 引擎,server/api/ 下的文件即服务端接口,可部署到 Node、Serverless 或边缘运行时,无需额外后端框架。
// server/api/posts.get.ts
export default defineEventHandler(async () => {
// 这里可直接查数据库或调用内部服务
return [
{ id: 1, title: 'Nuxt 3 入门' },
{ id: 2, title: 'Nitro 部署实战' }
]
})
// server/api/posts.post.ts 接收写入
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return { ok: true, received: body }
})
真实接口一定要做输入校验与错误处理,不要直接信任客户端 body。Nitro 支持在 server/middleware/ 放置全局中间件(如鉴权、日志、CORS),也支持用 zod 之类库校验 readBody 的结果后再入库。对于跨域场景,可参考 Nginx 反向代理 在网关层统一处理,避免在每个 server 路由里重复写头。Nitro 默认按部署目标自动选择运行时(Node、Bun、边缘),接口代码应保持对平台 API 的最小依赖,以便后续平滑切换部署形态。
六、SEO 与元信息:用 useSeoMeta 声明
Nuxt 3 的服务端渲染天然利于搜索引擎收录,配合 useSeoMeta 可在组件内声明 title、description 与 Open Graph 信息,无需手动操作 document.head。
// 在页面 setup 中声明
useSeoMeta({
title: 'Nuxt 3 实战教程',
description: '用 Vue 全栈框架打造 SSR 与边缘渲染应用',
ogTitle: 'Nuxt 3 实战教程',
ogDescription: '从起步到部署的完整示例',
ogType: 'article'
})
七、部署:Node、静态与边缘三种形态
同一份代码可输出不同产物:nuxi build 产出 Node 服务,nuxi generate 产出纯静态,配合预设还可编译到边缘函数。容器化部署可参考 Docker 容器化基础,CI 集成可参考 GitHub Actions 实战。
# 多阶段构建 Dockerfile
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"]
| 目标 | 命令 | 产物 | 适合 |
|---|---|---|---|
| Node 服务 | nuxi build | .output/server | 动态 SSR/API |
| 纯静态 | nuxi generate | .output/public | 文档/营销站 |
| 边缘 | nuxi build(cloudflare 预设) | 边缘函数 | 全球低延迟 |
八、生产检查清单
上线前建议逐项确认:① 路由与动态参数已测试;② 数据获取使用 useAsyncData 并带 key 去重;③ SEO 元信息已声明;④ 服务端 API 做好校验与限流;⑤ 错误页与 404 已配置;⑥ 构建产物与运行时 Node 版本一致;⑦ 静态资源走 CDN。开发体验上,VS Code 的高效配置 能显著提升 Nuxt 调试效率。
九、常见误区与排障
误区 1:在水合阶段重复请求。 用 useFetch 而非裸 $fetch,Nuxt 会自动在服务端预取并在客户端复用。误区 2:把浏览器 API 写进顶层 setup。 window/document 只能在 onMounted 或 process.client 守卫内访问,否则 SSR 会报错。误区 3:边缘部署忽略 Node 内置模块。 边缘运行时不保证支持全部 Node API,涉及文件系统或特定依赖时需显式标注 nitro.runtimeConfig 或改用兼容实现。
十、状态管理、插件与模块生态
复杂页面离不开跨组件状态,Nuxt 3 官方推荐 Pinia 作为状态库。它在 SSR 下会自动做状态水合,避免客户端拿到空 store 再闪烁加载。把全局状态抽成 store 后,页面组件只负责读取与触发 action,逻辑边界更清晰,也便于做服务端预取后直接喂给客户端。
// stores/counter.ts
import { defineStore } from 'pinia'
export const useCounter = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
inc() { this.count++ }
}
})
// 在组件中使用
const counter = useCounter()
counter.inc()
// plugins/my-plugin.ts 运行时注入全局能力
export default defineNuxtPlugin((nuxtApp) => {
return {
provide: {
hello: (name) => `hello ${name}`
}
}
})
// 组件内:const { $hello } = useNuxtApp()
除了手写插件,Nuxt 的模块生态是它最大的生产力来源:@nuxt/image 负责图片优化与懒加载,@pinia/nuxt 集成状态管理,@nuxtjs/color-mode 处理暗色模式,还有官方的 nuxt/content 把 Markdown 当数据源。安装模块后在 nuxt.config.ts 的 modules 数组里声明即可,框架会在构建期自动合并路由、组件与中间件,无需手动接线。相比从零拼装 Vite 插件,这种”约定优先”的方式显著降低了长期维护成本,也是 Nuxt 3 适合团队项目的核心原因。




