Nuxt: 页面与路由

最后更新:2026-08-26

Bob 在给 MegaShop 添加商品详情页时,手动配置了 50 条 vue-router 路由。新商品上架还得改路由文件——太痛苦了。Charlie 告诉他:Nuxt 3 用文件约定自动生成路由,创建文件就是创建路由。

1. 你将学到


2. 一个管理员的真实故事

(1) 痛点:手动路由配置噩梦

Bob 用传统 Vue Router 给 MegaShop 配了 1 thousand 条路由——每个分类、每个商品页都是一条。每次上架新品都要改路由文件,一次漏配就 404。Alice 找不到商品,投诉率飙升。

(2) Nuxt 文件路由的解法

Nuxt 3 的文件路由让 Bob 只需创建文件:

TEXT 📖 仅展示
pages/products/[id].vue  →  /products/:id

一百万商品只需一个文件,动态参数自动传递。

(3) 收益:零配置路由

Bob 再也不用写路由配置了,新增页面就是新建文件,1 million 商品详情页只需一个 [id].vue


3. 文件路由规则

(1) 目录到路由映射流程

100%
flowchart LR
    A[pages/index.vue] -->|"/"| B[Root Route]
    C[pages/about.vue] -->|"/about"| C2[About Route]
    D[pages/products/index.vue] -->|"/products"| D2[Product List Route]
    E[pages/products/[id].vue] -->|"/products/:id"| E2[Product Detail Route]
    F[pages/categories/[...slug].vue] -->|"/categories/:slug*"| F2[Catch-all Route]

(2) 文件路由映射规则

文件路径 生成路由 说明
pages/index.vue / 首页
pages/about.vue /about 静态页面
pages/products/index.vue /products 商品列表
pages/products/[id].vue /products/:id 动态参数
pages/categories/[slug].vue /categories/:slug 动态参数
pages/categories/[...slug].vue /categories/:slug(*) 通配路由
pages/404.vue /404 自定义 404

▶ 示例:基础页面路由

VUE
<!-- pages/index.vue -->
<template>
  <div>
    <h1>MegaShop - Millions of Products</h1>
    <NuxtLink to="/products">Browse Products</NuxtLink>
  </div>
</template>

输出:

TEXT 📖 仅展示
// 执行成功
VUE
<!-- pages/about.vue -->
<template>
  <div>
    <h1>About MegaShop</h1>
    <p>Serving over 1 million products worldwide</p>
  </div>
</template>

4. 动态路由

(1) 单参数动态路由

使用 [param] 语法定义动态路由参数。

▶ 示例:商品详情页动态路由

VUE
<!-- pages/products/[id].vue -->
<template>
  <div>
    <h1>Product #{{ route.params.id }}</h1>
    <p>Price: ${{ product?.price }} USD</p>
  </div>
</template>

<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
</script>

输出:

TEXT 📖 仅展示
// 执行成功

(2) 通配路由

使用 [...slug] 语法捕获多级路径。

▶ 示例:多级分类通配路由

VUE
<!-- pages/categories/[...slug].vue -->
<template>
  <div>
    <h1>Category: {{ slugPath }}</h1>
    <p>{{ products.length }} products found</p>
  </div>
</template>

<script setup lang="ts">
const route = useRoute()
// /categories/electronics/headphones → slug = ['electronics', 'headphones']
const slugPath = computed(() => (route.params.slug as string[]).join('/'))
const { data: products } = await useFetch(`/api/categories/${slugPath.value}`)
</script>

输出:

TEXT 📖 仅展示
// 执行成功

(3) 动态路由参数类型

语法 匹配 示例 URL params 值
[id] 单段 /products/123 { id: '123' }
[id].vue + index.vue 可选 /products 或 /products/123 { id?: '123' }
[...slug] 多段通配 /a/b/c { slug: ['a','b','c'] }

5. 嵌套路由

(1) 嵌套路由结构

嵌套路由需要父级页面(同名的 .vue)和子级目录配合:

TEXT 📖 仅展示
pages/
├── products/
│   ├── index.vue          # /products (child)
│   ├── [id].vue           # /products/:id (child)
│   └── edit.vue           # /products/edit (child)
└── products.vue           # /products (parent with <NuxtPage />)

▶ 示例:商品页嵌套路由

VUE
<!-- pages/products.vue - Parent layout -->
<template>
  <div>
    <nav class="product-nav">
      <NuxtLink to="/products">All Products</NuxtLink>
      <NuxtLink to="/products/featured">Featured</NuxtLink>
    </nav>
    <!-- Child routes render here -->
    <NuxtPage />
  </div>
</template>

输出:

TEXT 📖 仅展示
// 执行成功
VUE
<!-- pages/products/index.vue - Child: product list -->
<template>
  <div>
    <h1>All Products</h1>
    <p>1 million products available</p>
  </div>
</template>
VUE
<!-- pages/products/[id].vue - Child: product detail -->
<template>
  <div>
    <h1>Product Detail</h1>
    <p>ID: {{ route.params.id }}</p>
  </div>
</template>

(2) 嵌套路由 vs Layout 对比

维度 嵌套路由 Layout
机制 父页面 + NuxtPage slot layouts/ + NuxtLayout
路由关系 父子路由共享布局 页面级布局切换
数据获取 父子各自 useFetch Layout 不获取数据
适用场景 分类→商品→评论 层级 页面整体布局(Header/Footer)

6. 编程式导航

(1) 三种导航方式对比

API 用途 服务端可用 场景
navigateTo() 编程跳转 登录后重定向
useRouter().push() 客户端跳转 按钮点击导航
NuxtLink 声明式链接 模板中的导航

▶ 示例:navigateTo 服务端重定向

TYPESCRIPT
// In middleware or server-side context
export default defineNuxtRouteMiddleware((to) => {
  const isAuthenticated = useState('isAuthenticated')

  if (!isAuthenticated.value && to.path.startsWith('/admin')) {
    return navigateTo('/login', { redirectCode: 302 })
  }
})

输出:

TEXT 📖 仅展示
// 执行成功

▶ 示例:useRouter 客户端导航

VUE
<template>
  <button @click="goToProduct(product.id)">
    View Details
  </button>
</template>

<script setup lang="ts">
const router = useRouter()

function goToProduct(id: number) {
  router.push(`/products/${id}`)
}
</script>

输出:

TEXT 📖 仅展示
// 执行成功

▶ 示例:useRoute 获取参数

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

// Access params
const productId = route.params.id         // From [id].vue
const categorySlug = route.params.slug     // From [...slug].vue

// Access query params
// /products?category=electronics&sort=price
const category = route.query.category      // 'electronics'
const sort = route.query.sort              // 'price'
</script>

输出:

TEXT 📖 仅展示
// 执行成功

7. MegaShop 多级路由实战

(1) MegaShop 路由规划

URL 文件 功能
/ pages/index.vue 首页
/products pages/products/index.vue 商品列表
/products/[id] pages/products/[id].vue 商品详情
/categories/[category] pages/categories/[category].vue 分类页
/categories/[category]/[subcategory] pages/categories/[...slug].vue 子分类
/cart pages/cart.vue 购物车
/checkout pages/checkout.vue 结算页
/admin pages/admin/index.vue 管理后台
/admin/products pages/admin/products/index.vue 商品管理
/404 pages/404.vue 404 页面

▶ 示例:MegaShop 404 页面

VUE
<!-- pages/404.vue -->
<template>
  <div class="not-found">
    <h1>404 - Product Not Found</h1>
    <p>The product you're looking for doesn't exist.</p>
    <NuxtLink to="/products">Browse all products</NuxtLink>
  </div>
</template>

输出:

TEXT 📖 仅展示
// 执行成功

8. 综合示例:MegaShop 路由系统

VUE
<!-- pages/index.vue - Homepage -->
<template>
  <div>
    <section class="hero">
      <h1>MegaShop</h1>
      <p>Over 1 million products, delivered worldwide</p>
      <NuxtLink to="/products">Shop Now</NuxtLink>
    </section>
    <section class="featured">
      <ProductCard v-for="p in featured" :key="p.id" :product="p" />
    </section>
  </div>
</template>

<script setup lang="ts">
const { data: featured } = await useFetch('/api/products/featured')
</script>
VUE
<!-- pages/products/[id].vue - Product Detail -->
<template>
  <div v-if="product">
    <h1>{{ product.name }}</h1>
    <p class="price">${{ product.price }} USD</p>
    <button @click="addToCart">Add to Cart</button>
    <button @click="goBack">Back to List</button>
  </div>
  <div v-else>
    <p>Product not found</p>
    <NuxtLink to="/products">Browse Products</NuxtLink>
  </div>
</template>

<script setup lang="ts">
const route = useRoute()
const router = useRouter()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)

function addToCart() {
  navigateTo('/cart')
}

function goBack() {
  router.back()
}
</script>

❓ 常见问题

Q 动态路由的参数是字符串还是数字?
A URL 参数始终是字符串。需要数字时手动转换:Number(route.params.id) 或在 useFetch 时由 API 返回类型决定。
Q [id].vue 和 [...slug].vue 有什么区别?
A [id] 只匹配一段路径(/products/123),[...slug] 匹配多段(/a/b/c),slug 是数组。catch-all 路由适合无限层级分类。
Q 嵌套路由的父页面必须存在吗?
A 是的,父页面 products.vue 和子目录 products/ 同时存在时才形成嵌套。如果只要子路由,不要创建父页面。
Q NuxtLink 和普通 a 标签有什么区别?
A NuxtLink 做客户端导航(不刷新页面),a 标签做全页刷新。站内导航用 NuxtLink,外部链接用 a 标签。
Q 如何设置自定义 404 页面?
A 创建 pages/404.vue 即可。未匹配任何路由时 Nuxt 3 自动显示此页面。也可在 catch-all 路由中处理。
Q routeRules 和 pages 路由有什么关系?
A routeRules 在 nuxt.config.ts 中按路由路径配置渲染策略(SSR/ISR/CSR),不影响路由定义。路由由 pages/ 文件决定,routeRules 只控制渲染行为。

📖 小节


📝 作业

  1. 基础题(难度⭐):创建 pages/about.vue 和 pages/contact.vue,验证路由是否自动生成
  2. 进阶题(难度⭐⭐):实现 /products/[category]/[id] 两级动态路由,在页面中展示 category 和 id 参数
  3. 挑战题(难度⭐⭐⭐):实现一个 catch-all 路由 [...slug].vue,能够渲染 /docs/getting-started/installation 这样的多级路径,并显示面包屑导航

---|

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏