3、路由系统详解:文件路由、动态路由、嵌套路由、路由中间件、路由验证
路由,说白了就是 URL 和页面组件的映射关系。Nuxt3 的路由系统非常强大,而且几乎不需要手动配置。你只需要在 pages/ 目录下放文件,路由就自动生成了。我刚开始用的时候也觉得有点神奇,但用久了你会发现,这才是现代前端框架该有的样子。
3.1 文件路由:约定大于配置
Nuxt3 采用文件系统路由。什么意思呢?就是你的文件路径,直接决定了 URL 路径。
举个例子:
pages/
├── index.vue → /
├── about.vue → /about
├── blog/
│ ├── index.vue → /blog
│ └── post.vue → /blog/post
└── user/
└── profile.vue → /user/profile
看到没?pages/about.vue 自动对应 /about 路由。pages/blog/index.vue 对应 /blog。完全不需要你写 router 配置。
pages/index.vue,而不是 pages/home.vue。因为 Nuxt 默认把 index.vue 当作目录的入口页面。
3.2 动态路由:处理不确定的路径
实际项目中,我们经常需要处理动态参数。比如用户详情页 /user/123,文章详情页 /article/hello-world。这时候就需要动态路由。
在 Nuxt3 中,用 [] 方括号表示动态参数:
pages/
├── user/
│ └── [id].vue → /user/:id
└── article/
└── [slug].vue → /article/:slug
在组件中获取参数:
<script setup>
const route = useRoute()
console.log(route.params.id) // 如果是 /user/123,输出 123
console.log(route.params.slug) // 如果是 /article/hello,输出 hello
</script>
还可以有多层动态路由:
pages/
└── category/
└── [category]/
└── [id].vue → /category/:category/:id
route.params.id 是路径参数,route.query.id 是 URL 问号后面的参数。两者完全不同。
3.3 嵌套路由:页面里套页面
嵌套路由适合做「父页面 + 子页面」的布局。比如后台管理系统,左侧菜单固定,右侧内容根据路由切换。
实现方式很简单:在父级目录下放一个 parent.vue,然后在同目录下建一个同名文件夹,里面放子页面。
pages/
├── dashboard.vue → /dashboard
└── dashboard/
├── index.vue → /dashboard (默认子路由)
├── settings.vue → /dashboard/settings
└── users.vue → /dashboard/users
在 dashboard.vue 中,必须加上 <NuxtPage /> 组件,子页面才会渲染:
<template>
<div>
<h1>仪表盘</h1>
<aside>
<!-- 左侧菜单 -->
<NuxtLink to="/dashboard/settings">设置</NuxtLink>
<NuxtLink to="/dashboard/users">用户管理</NuxtLink>
</aside>
<main>
<NuxtPage /> <!-- 子页面在这里渲染 -->
</main>
</div>
</template>
我个人习惯把 <NuxtPage /> 放在 <transition> 里,这样切换子页面时还能加个动画效果。
3.4 路由中间件:守卫你的页面
路由中间件,说白了就是在进入页面之前执行一段逻辑。最常见的场景就是「登录验证」——没登录就跳转到登录页。
Nuxt3 的中间件放在 middleware/ 目录下:
middleware/
├── auth.ts → 全局中间件
└── guest.ts → 命名中间件
写一个简单的 auth 中间件:
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
const token = useCookie('token')
if (!token.value) {
// 没登录,跳转到登录页
return navigateTo('/login')
}
})
使用方式有两种:
- 全局中间件:文件名以
.global.ts结尾,所有路由都会触发 - 命名中间件:在页面组件中手动指定
在页面中指定中间件:
<script setup>
definePageMeta({
middleware: 'auth' // 对应 middleware/auth.ts
})
</script>
to.path,结果页面跳转乱了。正确的做法是用 navigateTo() 或 abortNavigation(),不要手动修改路由对象。
3.5 路由验证:把不合法请求挡在门外
路由验证和中间件有点像,但更专注于「参数合法性」的检查。比如动态路由 /user/[id],如果 id 不是数字,直接返回 404。
在页面组件中使用 validate 属性:
<script setup>
definePageMeta({
validate: (route) => {
// 验证 id 是否为数字
return /^\d+$/.test(route.params.id)
}
})
</script>
如果验证失败,Nuxt3 会自动返回 404 页面。你也可以自定义错误处理:
definePageMeta({
validate: (route) => {
if (!/^\d+$/.test(route.params.id)) {
throw createError({ statusCode: 404, message: '用户不存在' })
}
return true
}
})
你想想看,如果没有路由验证,用户输入 /user/abc,后端接口可能直接报 500 错误。有了验证,404 页面优雅地告诉用户「你找的人不存在」。用户体验完全不一样。
3.6 实战建议:路由设计的最佳实践
| 场景 | 推荐做法 | 不推荐做法 |
|---|---|---|
| 静态页面 | 直接放 pages/ 下 |
手动写 router 配置 |
| 动态参数 | 用 [param].vue |
用查询参数传 ID |
| 权限控制 | 用路由中间件 | 在每个页面里写 if 判断 |
| 参数校验 | 用 validate |
在组件 mounted 里校验 |
/user/123/profile/settings 这种,完全可以拆成 /user/123 和 /settings 两个独立路由。
嗯,路由系统就讲到这里。记住一句话:文件即路由,约定大于配置。你只要把文件放对位置,Nuxt3 会自动帮你搞定一切。下一章我们聊聊数据获取,那才是服务端渲染的重头戏。