Nuxt新手教程

👁 11

第 1 步:先搞懂 Nuxt 解决了什么问题

在动手写代码之前,值得花两分钟搞清楚 Nuxt 的定位。

Vue 本身是个客户端渲染(CSR)框架。浏览器拿到一个近乎空的 HTML 壳子,然后下载并执行 JS 代码,才把页面“填”出来。这样做的问题是:

  1. 首屏慢:用户得等 JS 下载、解析、执行完才能看到内容。
  2. SEO 差:搜索引擎爬虫抓取到的几乎是空页面。

Nuxt 3 则提供了服务端渲染(SSR)静态站点生成(SSG)等能力。它能在服务端把页面提前渲染成完整的 HTML 再发给浏览器,首屏秒开,SEO 也直接搞定。

而且它还内置了文件系统路由、自动导入、Vite 构建工具、Nitro 服务引擎……说白了,Vue 项目里你以前要手动配置的那一堆东西,Nuxt 都帮你自动搞定了。

对于 2026 年的 Nuxt 3 来说,最稳定的版本是 v3.11+,Node.js 版本要求 18.x 或更高


第 2 步:初始化项目,感受“开箱即用”

2.1 创建项目

用官方工具 nuxi 一行命令搞定,不需要任何交互式问答:

bash

npx nuxi@latest init my-nuxt-app
cd my-nuxt-app
npm install
npm run dev

浏览器打开 http://localhost:3000,看到欢迎页就说明环境跑通了。

2.2 看一眼核心目录结构

初始化好的项目默认只有几个文件,但我们需要知道往哪里加东西。这是 Nuxt 3 的约定目录结构,记住它你就掌握了 80% 的使用方法:

text

my-nuxt-app/
├── app.vue               # 应用入口(根组件)
├── nuxt.config.ts       # Nuxt 配置文件
├── pages/               # 页面目录 → 自动生成路由(核心!)
├── components/           # 组件目录 → 自动全局导入
├── composables/         # 组合式函数目录 → 自动导入
├── layouts/             # 布局目录
├── server/               # 服务端 API 和中间件(全栈能力)
├── public/               # 静态资源(不经过构建)
├── assets/               # 需要构建的静态资源(CSS、图片等)
└── stores/               # Pinia 状态管理(需额外安装)

2.3 把欢迎页替换成自己的页面

修改 app.vue,把 <NuxtWelcome /> 替换成 <NuxtPage />,这样它就会去渲染 pages/ 目录下的页面了:

vue

<!-- app.vue -->
<template>
<NuxtLayout>
  <NuxtPage />
</NuxtLayout>
</template>

然后在 pages/ 目录下新建 index.vue

vue

<!-- pages/index.vue -->
<template>
<div>
  <h1>Hello Nuxt 3</h1>
  <p>我的第一个 Nuxt 页面</p>
</div>
</template>

刷新页面,内容就变了。这就是 Nuxt 的文件系统路由——你不用配置任何 router.js,文件放在 pages/ 下,路径就自动映射好了。


第 3 步:吃透 Vue 3 在 Nuxt 里的用法

Nuxt 3 完全继承了 Vue 3 的能力,你在 Vue 项目里怎么写的组件、组合式 API,在 Nuxt 里都一样写。组件结构依然是 <template> + <script setup> + <style> 三段式:

vue

<template>
<div>
  <p>计数: {{ count }}</p>
  <button @click="increment">+1</button>
</div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const count = ref(0)
const increment = () => count.value++

onMounted(() => {
console.log('组件已挂载')
})
</script>

<style scoped>
button {
padding: 8px 16px;
cursor: pointer;
}
</style>

唯一要注意的点:Nuxt 3 在服务端也会执行组件代码(SSR 场景)。如果你的代码里用了 windowdocument 这些浏览器对象,需要放在 onMounted 里,或者用 <ClientOnly> 组件包裹。


第 4 步:路由系统——约定大于配置

Nuxt 3 最爽的一点就是路由。你只管在 pages/ 下建文件,路由自动生成。

4.1 基础路由

text

pages/
├── index.vue       → /
├── about.vue       → /about
└── blog/
  └── index.vue   → /blog

4.2 动态路由

用方括号 [param] 表示接受动态参数:

text

pages/
└── blog/
  └── [id].vue   → /blog/1, /blog/2, ...

在页面里获取参数:

vue

<script setup>
const route = useRoute()
const id = route.params.id // 类型是 string
</script>

4.3 页面跳转

<NuxtLink> 组件代替 <a> 标签,实现 SPA 级别的无刷新跳转:

vue

<template>
<NuxtLink to="/about">关于我们</NuxtLink>
<NuxtLink :to="`/blog/${postId}`">查看文章</NuxtLink>
</template>

4.4 页面元信息(definePageMeta)

在页面组件里用 definePageMeta 配置标题、布局、中间件等:

vue

<script setup>
definePageMeta({
title: '博客详情',
layout: 'custom', // 使用自定义布局
requiresAuth: true // 自定义字段,可在路由守卫中判断
})
</script>

第 5 步:数据获取——SSR 场景下的正确姿势

在 Nuxt 里,数据获取不能用普通的 fetchaxios 直接写,而是要用它提供的几个专用函数,确保数据在服务端和客户端都能正确获取

5.1 useFetch(最常用,推荐)

useFetchuseAsyncData + $fetch 的封装,最简洁、最常用

vue

<script setup>
interface Post {
id: number
title: string
}

const { data, pending, error, refresh } = await useFetch<Post[]>('/api/posts')
</script>

<template>
<div v-if="pending">加载中...</div>
<div v-else-if="error">出错了:{{ error.message }}</div>
<ul v-else>
<li v-for="post in data" :key="post.id">{{ post.title }}</li>
</ul>
</template>

5.2 useAsyncData(需要更灵活控制时用)

当你的数据获取逻辑比较复杂(比如需要组合多个请求、做数据转换)时,用 useAsyncData + $fetch 的组合:

<script setup>
const { data, refresh } = await useAsyncData('unique-key', async () => {
// 可以写更复杂的逻辑
const res = await $fetch('/api/posts')
return res.filter(post => post.published)
}, {
watch: [], // 监听依赖变化自动刷新
staleTime: 60000 // 缓存 60 秒
})
</script>

5.3 $fetch(纯客户端请求用)

$fetch 是 Nuxt 内置的通用请求工具,但如果直接用在组件顶层,数据不会在服务端获取。一般用在用户交互触发的请求里:

vue

<script setup>
const submitForm = async () => {
const result = await $fetch('/api/submit', {
method: 'POST',
body: { name: '张三' }
})
console.log(result)
}
</script>

💡 一个核心原则:需要 SSR 的数据用 useFetchuseAsyncData,纯客户端交互用 $fetch


第 6 步:组件与组合式函数的自动导入——少写 80% 的 import

这是 Nuxt 3 开发体验提升最大的功能。

6.1 组件自动导入

components/ 目录下的所有 .vue 文件,无需 import,直接在模板里用标签名引用

text

components/
├── Button.vue → <Button />
├── blog/
│ └── PostCard.vue → <BlogPostCard />
└── ui/
└── Modal.vue → <UiModal />

页面里直接用:

vue

<template>
<div>
<BlogPostCard :post="post" />
<UiModal v-model:open="showModal" />
</div>
</template>

6.2 组合式函数自动导入

composables/ 目录下的函数也会自动导入。创建一个 composables/useCounter.ts

typescript

export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const increment = () => count.value++
const decrement = () => count.value--
return { count, increment, decrement }
}

在组件里直接用,不需要 import:

vue

<script setup>
const { count, increment } = useCounter(10)
</script>

⚠️ 注意:

1、但是在utils文件下面的.ts文件需要导入才能使用

2、只有当文件导出了具名函数时,自动导入才生效。默认导出不会自动导入。


第 7 步:布局系统——统一页面的骨架

布局用来抽离多个页面的公共部分(导航栏、页脚、侧边栏)。

7.1 默认布局

layouts/default.vue 里定义全局布局:

vue

<!-- layouts/default.vue -->
<template>
<div class="app-layout">
<header>
<nav>导航栏</nav>
</header>
<main>
<slot /> <!-- 这里是页面的具体内容 -->
</main>
<footer>页脚</footer>
</div>
</template>

所有页面默认套用这个布局。

7.2 自定义布局

layouts/admin.vue 定义后台管理布局,然后在页面里指定使用它:

vue

<script setup>
definePageMeta({
layout: 'admin'
})
</script>

7.3 不使用布局

某些页面(如登录页)不需要布局,可以关闭:

vue

<script setup>
definePageMeta({
layout: false
})
</script>

第 8 步:状态管理——Pinia 是首选

虽然 Nuxt 内置了 useState 做轻量级全局状态,但正式项目推荐用 Pinia,生态成熟、DevTools 支持好。

8.1 安装

bash

npm install @pinia/nuxt pinia

nuxt.config.ts 中注册模块:

typescript

export default defineNuxtConfig({
modules: ['@pinia/nuxt']
})

8.2 定义 Store

stores/user.ts 中定义用户 store:

typescript

import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
state: () => ({
id: '',
name: '',
email: '',
token: ''
}),
getters: {
isLoggedIn: (state) => !!state.token
},
actions: {
async login(email: string, password: string) {
const res = await $fetch('/api/auth/login', {
method: 'POST',
body: { email, password }
})
this.$patch({
id: res.user.id,
name: res.user.name,
email: res.user.email,
token: res.token
})
},
logout() {
this.$reset()
}
}
})

8.3 在组件中使用

vue

<script setup>
import { useUserStore } from '~/stores/user'

const userStore = useUserStore()

const handleLogin = async () => {
await userStore.login('user@example.com', 'password')
}
</script>

<template>
<div v-if="userStore.isLoggedIn">
欢迎,{{ userStore.name }}
<button @click="userStore.logout">退出</button>
</div>
<button v-else @click="handleLogin">登录</button>
</template>

第 9 步:服务端 API——全栈开发的“意外收获”

Nuxt 3 内置了 Nitro 服务引擎,你可以在 server/ 目录里直接写后端接口,不需要单独启动一个 Node 服务。

9.1 创建一个 API 接口

server/api/posts/index.get.ts 中:

typescript

export default defineEventHandler(async (event) => {
// 模拟从数据库获取数据
const posts = [
{ id: 1, title: 'Nuxt 3 入门指南' },
{ id: 2, title: 'Vue 3 组合式 API 详解' }
]
return posts
})

前端直接 useFetch('/api/posts') 就能拿到数据。

9.2 带参数的 API

server/api/posts/[id].get.ts

typescript

export default defineEventHandler(async (event) => {
const id = getRouterParam(event, 'id')
// 根据 id 查询数据
return { id, title: `文章 ${id}` }
})

9.3 服务器中间件(如鉴权)

server/middleware/auth.ts

typescript

export default defineEventHandler((event) => {
const token = getHeader(event, 'authorization')
if (!token) {
setResponseStatus(event, 401)
return { error: '未授权' }
}
// 验证 token...
})

这样,你就在同一个项目里搞定了前后端,部署也只需要一个命令 npm run build,Nitro 会自动打包成适配各种环境的部署文件。


第 10 步:性能优化——让项目跑得更快

10.1 用 computed 代替方法

在模板里调用方法,每次重新渲染都会重新计算。用 computed 会缓存结果:

typescript

// ❌ 坏:每次渲染都重新计算
const getActiveItems = () => items.value.filter(i => i.active)

// ✅ 好:只有 items 变化时才重新计算
const activeItems = computed(() => items.value.filter(i => i.active))

10.2 大列表用 v-memo

对于长列表,用 v-memo 让 Vue 只重新渲染变化的部分:

vue

<div
v-for="item in items"
:key="item.id"
v-memo="[item.id, item.updated]"
>
<ExpensiveComponent :data="item" />
</div>

10.3 组件懒加载

体积大的组件(图表、富文本编辑器等)用 defineAsyncComponent 懒加载:

typescript

const HeavyChart = defineAsyncComponent({
loader: () => import('~/components/HeavyChart.vue'),
loadingComponent: LoadingSpinner,
delay: 200
})

10.4 渲染模式按需配置

nuxt.config.ts 里针对不同路由配置不同的渲染模式:

typescript

export default defineNuxtConfig({
routeRules: {
'/': { prerender: true }, // 首页 SSG
'/blog/**': { swr: 3600 }, // 博客页 ISR,缓存 1 小时
'/admin/**': { ssr: false }, // 后台管理纯 CSR
'/api/**': { cors: true } // API 接口允许跨域
}
})

第 11 步:打包与部署——从开发到上线

11.1 构建命令

bash

# SSR 服务端部署(默认)
npm run build

# SSG 静态站点生成
npm run generate

11.2 部署方式

SSR 模式:运行 npm run build 后,.output/ 目录就是部署产物。用 Node.js 启动即可:

bash

node .output/server/index.mjs

SSG 模式:运行 npm run generate 后,dist/ 目录是纯静态文件,可以直接托管到 Nginx、Vercel、Netlify 或阿里云 OSS。


写在最后

从新手到熟练,核心不在于背 API,而在于理解 Nuxt 的核心理念:约定优于配置。你不需要花时间纠结路由怎么配、webpack 怎么改、SSR 怎么搞——Nuxt 已经把最佳实践封装好了,你只要按规矩来,就能写出性能好、可维护性强的 Vue 应用。

最后给你一个可以循序渐进的学习路径:

  1. 先用 nuxi init 搭个项目,把 pages/components/ 玩熟;
  2. 然后用 useFetch 接一个真实 API,感受 SSR 数据获取;
  3. 最后试着在 server/ 里写几个接口,体验一把“单项目全栈开发”的爽感。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部