5.1 文件路由与动态嵌套


5.1 文件路由与动态嵌套

浏览器持棒后跑的是哪条道?答案是构建期就画好的路由表。本节讲 pages 目录的命名语法如何推导路由——动态段、可选段、嵌套、catch-all 全集,末尾给校验路由表的方法。它是 5.2 守卫与 5.3 导航的地基,也是 Nuxt"约定优于配置"最密集的舞台。

推导规则速查

文件路径即路由地址,特殊命名有特殊含义:

目录/文件 生成的路由 说明
pages/index.vue / 首页
pages/about.vue /about 静态路由
pages/products/index.vue /products 目录式首页
pages/products/[id].vue /products/:id 动态段,id 是参数
pages/products-[group].vue /products-:group() 前缀拼接动态段(较新语法)
pages/products/[id]/reviews.vue /products/:id/reviews 动态段的下级
pages/[[slug]].vue / 与 /:slug 双匹配 可选动态段
pages/users-[id]/profile.vue /users-:id/profile 组合示例
pages/[...slug].vue /:slug(.) catch-all,兜底 404 之前的自定义页

动手搭一套商品站路由:

pages/ ├── index.vue # / ├── about.vue # /about ├── products/ │ ├── index.vue # /products │ └── [id].vue # /products/123 └── docs/[...slug].vue # /docs/a/b/c 任意深度

动态段页面里取参数:

<!-- pages/products/[id].vue --> <script setup> const route = useRoute() const id = computed(() => route.params.id) const { data: product } = await useFetch(() => `/api/products/${id.value}`, { key: () => `product-${id.value}`, // 参数进 key watch: [id], // 同一组件实例下 id 变化自动重取 }) </script> <template> <div v-if="product"> <h1>{{ product.name }}</h1> <p>编号:{{ id }}</p> </div> </template>

watch: [id] 值得注意:从 /products/1 点进 /products/2 时,两个地址匹配同一个页面组件,组件不销毁重建,只有参数变化——不监听 id 的话,数据停在旧商品上,这是内容站最常见的"换页不换数据"事故。

图 5-1:路由表推导示意

图 5-1:路由表推导示意

嵌套路由:父目录与父文件成对

嵌套路由的规则稍绕:同名目录 + 同名文件构成父子关系。products.vue 与 products/ 目录同时存在时,products.vue 是父组件(负责外框),目录内的页面是子组件(填进 NuxtPage):

<!-- pages/products.vue:父路由,提供商品域的外框 --> <template> <div class="products-shell"> <CategoryNav /> <NuxtPage /> <!-- 子路由页面渲染在这里 --> </div> </template>
pages/ ├── products.vue # 父:/products 外框 └── products/ ├── index.vue # 子:/products 首页填进父的 NuxtPage └── [id].vue # 子:/products/123 详情填进父的 NuxtPage

没有 products.vue 时,products/ 下的页面各自全页渲染——目录只是分组,不产生嵌套。要不要父组件,取决于"这组页面是否共享一个外框"。

校验与排错

路由表不必靠脑内推导验证。开发模式下访问任意路径,DevTools 的路由面板直接展示记录;也可以在插件里打印:

// plugins/debug-routes.ts(开发环境专用) export default defineNuxtPlugin(() => { if (import.meta.dev) { const router = useRouter() console.table(router.getRoutes().map(r => ({ path: r.path, name: r.name, }))) } })

排错两条经验:其一,页面不生效先查文件名拼写([id] 写成 (id)... 少一个点都是静默失败);其二,路由优先级是"具体路径优先于动态段、动态段优先于 catch-all",同时存在 about.vue 与 [slug].vue 时 /about 走静态页。

⚠️ 常见坑:动态参数页面忘记 watch。/products/1 到 /products/2 组件复用不重建,useFetch 不监听参数就永远显示第一个商品。参数一律进 key 并加 watch。

💡 关键直觉:把 pages 目录当数据库的"路由 schema"读——文件名是主键语法,方括号是通配符,父子目录是外键。schema 定稿于构建期,运行期只查表不建表。

组织大规模页面的目录技巧

页面过百后,扁平的 pages 目录会失控。三个组织技巧:其一,用目录分组业务域(products/、orders/、account/ 各自成林),路由深度与信息架构对齐,新人找页面像查目录;其二,catch-all 只留一个兜底(比如 docs 目录下的 [...slug]),全站多个 catch-all 互相遮蔽是难查的路由 bug;其三,废弃页面用重定向而不是删文件——路由的旧地址可能已被外部引用与收录,在服务端配置旧路径到新路径的 301(Nitro 路由规则一行搞定),SEO 与用户体验双保全。

本节要点回顾

  • 推导规则:路径即地址;[id] 动态段、[[slug]] 可选段、[...slug] catch-all、users-[group] 拼接段;
  • 参数复用陷阱:同一动态页面间导航不重建组件,参数变化需 watch + key 编排;
  • 嵌套要成对:父文件与同名目录同时存在才嵌套,父组件里放 NuxtPage;
  • 优先级:静态 > 动态 > catch-all,重名时具体者胜;
  • 验证手段:DevTools 路由面板或开发插件打印全表,别靠猜。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U