Nuxt 生态 精选推荐

Nuxt 3 快速入门指南

HTMLPAGE 团队
8 分钟阅读

从零开始学习 Nuxt 3,包括项目初始化、文件结构、自动路由、布局系统、数据获取方法和服务器端渲染基础。适合想要快速上手 Nuxt 的开发者。

#Nuxt 3 #Vue 3 #SSR #全栈开发

Nuxt 3 快速入门指南

什么是 Nuxt 3

Nuxt 3 是基于 Vue 3 的框架,提供了服务器端渲染(SSR)、静态生成(SSG)、混合渲染等能力,让开发者可以更方便地构建全栈 Web 应用。

Nuxt 的核心优势:

┌──────────────────────────────────┐
│ 1. 一站式解决方案                 │
├──────────────────────────────────┤
│ ✅ Vue 3 组件框架
│ ✅ 服务器渲染(SSR)
│ ✅ 静态生成(SSG)
│ ✅ API 路由
│ ✅ 自动代码分割
│ ✅ 自动路由生成
│ ✅ 中间件系统
│ ✅ 插件系统
└──────────────────────────────────┘

1. 项目初始化

1.1 创建项目

# 使用官方脚手架
npx nuxi init my-nuxt-app

# 进入项目
cd my-nuxt-app

# 安装依赖
npm install

# 启动开发服务器
npm run dev

1.2 package.json 脚本

{
  "scripts": {
    "dev": "nuxt dev",              // 开发模式(http://localhost:3000)
    "build": "nuxt build",           // 编译生产版本
    "preview": "nuxt preview",       // 预览生产构建
    "generate": "nuxt generate",     // 静态生成
    "postinstall": "nuxt prepare"    // 生成 TypeScript 配置
  }
}

2. 项目文件结构

2.1 标准结构

my-nuxt-app/
├── .nuxt/                  # 自动生成,包含编译产物
├── app.vue                 # 根组件
├── nuxt.config.ts         # Nuxt 配置文件
├── package.json
│
├── pages/                 # 页面组件(自动生成路由)
│   ├── index.vue         # / 路由
│   ├── about.vue         # /about 路由
│   └── product/
│       └── [id].vue      # /product/:id 路由
│
├── components/           # 可复用组件(自动导入)
│   ├── Header.vue
│   ├── Footer.vue
│   └── Product/
│       └── Card.vue
│
├── layouts/             # 布局组件
│   ├── default.vue      # 默认布局
│   └── admin.vue        # 管理员布局
│
├── server/              # 服务器代码
│   └── api/
│       ├── products.ts
│       └── hello.ts
│
├── composables/         # 组合函数(自动导入)
│   ├── useAuth.ts
│   └── useFetch.ts
│
├── utils/               # 工具函数(自动导入)
│   └── helpers.ts
│
├── middleware/          # 路由中间件
│   ├── auth.ts
│   └── admin.ts
│
├── plugins/             # 插件
│   ├── vue-toastr.client.ts
│   └── analytics.ts
│
└── public/              # 静态资源
    ├── favicon.ico
    └── logo.svg

2.2 nuxt.config.ts 配置

export default defineNuxtConfig({
  // 生成目标
  ssr: true,  // 启用 SSR,设为 false 变成 SPA
  
  // 开发配置
  devtools: { enabled: true },  // Nuxt DevTools
  
  // CSS 和样式
  css: [
    '@/assets/styles/main.css'
  ],
  
  // 模块
  modules: [
    '@nuxt/content',      // 内容管理
    '@nuxt/image',        // 图片优化
    '@pinia/nuxt',        // 状态管理
    'nuxt-icon'           // 图标库
  ],
  
  // 路由
  router: {
    options: {
      hashMode: false     // URL 模式
    }
  },
  
  // 性能
  nitro: {
    prerender: {
      crawlLinks: true,
      routes: ['/sitemap.xml']
    }
  }
})

3. 自动路由系统

3.1 基本路由

页面文件结构       生成的路由
pages/
├── index.vue          →  /
├── about.vue          →  /about
├── contact.vue        →  /contact
└── product/
    └── index.vue      →  /product

3.2 动态路由

pages/
├── product/
│   ├── [id].vue       →  /product/:id
│   └── [id]/
│       └── review.vue →  /product/:id/review
│
├── user/
│   └── [id]/
│       └── settings.vue →  /user/:id/settings

3.3 路由参数访问

<!-- pages/product/[id].vue -->
<template>
  <div>
    <h1>产品 {{ id }}</h1>
    <p>{{ product?.name }}</p>
  </div>
</template>

<script setup lang="ts">
// 自动获取路由参数
const route = useRoute()
const id = route.params.id

// 获取查询参数
const search = route.query.search

// 编程式导航
const router = useRouter()

const goHome = () => {
  router.push('/')
}

const goProductDetail = (productId: number) => {
  router.push(`/product/${productId}`)
}
</script>

4. 布局系统

4.1 创建布局

<!-- layouts/default.vue -->
<template>
  <div>
    <Header />
    <main>
      <!-- 页面内容插入这里 -->
      <slot />
    </main>
    <Footer />
  </div>
</template>

<script setup lang="ts">
import Header from '@/components/Header.vue'
import Footer from '@/components/Footer.vue'
</script>

<style scoped>
main {
  min-height: 100vh;
  padding: 20px;
}
</style>

4.2 在页面中使用布局

<!-- pages/index.vue -->
<template>
  <div>
    <h1>首页</h1>
    <p>内容</p>
  </div>
</template>

<script setup lang="ts">
// 使用默认布局(自动)
definePageMeta({
  layout: 'default'
})

// 或使用自定义布局
// definePageMeta({
//   layout: 'admin'
// })
</script>

5. 数据获取

5.1 useFetch(Nuxt 推荐)

<template>
  <div>
    <p v-if="pending">加载中...</p>
    <div v-else-if="data">
      <h1>{{ data.title }}</h1>
      <p>{{ data.description }}</p>
    </div>
    <p v-if="error">错误:{{ error.message }}</p>
  </div>
</template>

<script setup lang="ts">
// useFetch 在 SSR 和客户端都能工作
const { data, pending, error, refresh } = await useFetch('/api/product/1')

// 重新获取数据
const reloadData = () => {
  refresh()
}
</script>

5.2 useAsyncData

<template>
  <div>
    <p v-if="pending">加载中...</p>
    <p v-else>{{ product?.name }}</p>
  </div>
</template>

<script setup lang="ts">
const route = useRoute()

// 手动异步操作
const { data: product, pending } = await useAsyncData(
  `product-${route.params.id}`,  // 缓存键
  async () => {
    const response = await $fetch(`/api/product/${route.params.id}`)
    return response
  }
)
</script>

5.3 $fetch

<template>
  <div>
    <button @click="loadData">加载数据</button>
    <p v-if="data">{{ data.name }}</p>
  </div>
</template>

<script setup lang="ts">
const data = ref(null)

// 单独的数据请求(通常用于事件处理)
const loadData = async () => {
  try {
    data.value = await $fetch('/api/product/1')
  } catch (error) {
    console.error('获取失败:', error)
  }
}
</script>

6. 服务器端路由

6.1 创建 API 路由

// server/api/products.ts
export default defineEventHandler(async (event) => {
  // GET /api/products
  return [
    { id: 1, name: '产品 A', price: 99 },
    { id: 2, name: '产品 B', price: 199 }
  ]
})

// server/api/products/[id].ts
export default defineEventHandler(async (event) => {
  // GET /api/products/:id
  const id = getRouterParam(event, 'id')
  
  return {
    id,
    name: `产品 ${id}`,
    price: 99 + parseInt(id) * 10
  }
})

// server/api/products.post.ts
export default defineEventHandler(async (event) => {
  // POST /api/products
  const body = await readBody(event)
  
  return {
    success: true,
    data: body
  }
})

6.2 在客户端调用

<template>
  <div>
    <button @click="fetchProducts">获取产品</button>
    <ul>
      <li v-for="product in products" :key="product.id">
        {{ product.name }} - ¥{{ product.price }}
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
const products = ref([])

const fetchProducts = async () => {
  products.value = await $fetch('/api/products')
}
</script>

7. 组合函数和工具

7.1 创建组合函数

// composables/useProduct.ts
export const useProduct = () => {
  const products = ref([])
  const loading = ref(false)

  const fetchProducts = async () => {
    loading.value = true
    try {
      products.value = await $fetch('/api/products')
    } finally {
      loading.value = false
    }
  }

  return {
    products: readonly(products),
    loading: readonly(loading),
    fetchProducts
  }
}

7.2 在组件中使用

<template>
  <div>
    <button @click="fetchProducts">加载产品</button>
    <p v-if="loading">加载中...</p>
    <ul v-else>
      <li v-for="product in products" :key="product.id">
        {{ product.name }}
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
// 自动导入(无需 import)
const { products, loading, fetchProducts } = useProduct()

onMounted(() => {
  fetchProducts()
})
</script>

8. 中间件系统

8.1 创建中间件

// middleware/auth.ts
export default defineRouteMiddleware((to, from) => {
  // 检查认证状态
  const authStore = useAuthStore()
  
  if (!authStore.isLoggedIn) {
    return navigateTo('/login')
  }
})

// middleware/admin.ts
export default defineRouteMiddleware((to, from) => {
  const authStore = useAuthStore()
  
  if (!authStore.isAdmin) {
    return abortNavigation('无权访问')
  }
})

8.2 使用中间件

<!-- pages/dashboard.vue -->
<template>
  <div>
    <h1>用户仪表板</h1>
  </div>
</template>

<script setup lang="ts">
definePageMeta({
  middleware: 'auth'  // 应用中间件
})
</script>

<!-- pages/admin/index.vue -->
<template>
  <div>
    <h1>管理员面板</h1>
  </div>
</template>

<script setup lang="ts">
definePageMeta({
  middleware: ['auth', 'admin']  // 多个中间件
})
</script>

9. 常用模块

9.1 Pinia 状态管理

// stores/product.ts
import { defineStore } from 'pinia'

export const useProductStore = defineStore('product', () => {
  const products = ref([])
  const selectedProduct = ref(null)

  const setProducts = (data) => {
    products.value = data
  }

  const selectProduct = (id: number) => {
    selectedProduct.value = products.value.find(p => p.id === id)
  }

  return {
    products,
    selectedProduct,
    setProducts,
    selectProduct
  }
})

9.2 @nuxt/content 内容管理

<template>
  <div>
    <article>
      <h1>{{ article.title }}</h1>
      <ContentRenderer :value="article" />
    </article>
  </div>
</template>

<script setup lang="ts">
// 获取内容
const article = await queryContent('blog/first-post').findOne()

// 或查询多个
const posts = await queryContent('blog').find()
</script>

10. Nuxt 3 最佳实践

// ✅ 最佳实践

// 1. 使用 TypeScript
interface Product {
  id: number
  name: string
  price: number
}

// 2. 使用自动导入的功能
// - 无需 import,直接使用 useState、useRoute、useRouter
// - 组件、中间件、插件自动导入

// 3. 服务器端代码与客户端分离
// - server/ 目录中的代码只在服务器运行
// - .server. 后缀的文件只在服务器运行

// 4. 正确处理 SSR
// 避免在 SSR 时访问浏览器 API
const handleClient = () => {
  if (process.client) {
    // 仅在客户端运行
    window.alert('Hello')
  }
}

// 5. 使用 SEO 组件
// 在 head 中定义元数据
useHead({
  title: '产品列表',
  meta: [
    { name: 'description', content: '查看所有产品' }
  ]
})

11. 部署

11.1 生产构建

# 构建用于 Node.js 服务器
npm run build

# 预览构建
npm run preview

# 静态生成(SSG)
npm run generate

11.2 部署选项

平台说明命令
Vercel官方推荐,一键部署连接 Git 即可
Netlify支持 SSG 和 SSR连接 Git 即可
Node.js 服务器自托管node .output/server/index.mjs
Docker容器化部署创建 Dockerfile

总结

Nuxt 3 的核心优势:

  • ✅ 开发体验优秀
  • ✅ 自动路由和代码分割
  • ✅ 完整的 SSR 支持
  • ✅ 丰富的模块生态
  • ✅ 很好的 SEO 支持

何时使用 Nuxt 3:

  • 需要 SEO 的应用
  • 需要快速开发
  • 习惯 Vue 的开发者
  • 需要全栈能力

推荐资源