# 路由
Rue 提供 `@rue-js/router` 作为官方路由方案,默认使用 Hash 模式,支持命名参数、子路由、命名路由、重定向与导航守卫。
可交互示例:访问 [路由 Demo](/examples/router-demo/guide/router/overview),可以直接体验嵌套路由、命名路由、`replace` 和 `beforeEnter` 守卫。
- 创建并安装 Router
- 定义路由与参数匹配
- 在视图中渲染与导航
- 使用运行时 API 获取当前路由
```tsx
import { type FC, useApp } from '@rue-js/rue'
import { createRouter, createWebHashHistory, RouterView, RouterLink } from '@rue-js/router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/docs/:id(\\d+)', component: DocDetail }, // 命名参数 + 正则
],
})
const RootApp: FC = () => (
)
useApp(RootApp).use(router).mount('#app')
```
## 历史模式
- `createWebHashHistory()`:基于 `location.hash` 的浏览器历史实现。无 `#` 时会自动跳转到 `#/`,并标准化位置字符串。
- `push('/path')` 与 `replace('/path')` 会更新 `hash`,并主动触发 `hashchange` 事件,确保响应式状态立即同步。
- `router.push()` 与 `router.replace()` 返回 Promise;成功时结果为 `undefined`,被取消、竞争掉或重复导航时会返回导航失败对象。
## 路由记录与参数匹配
- 路由记录类型:`{ path, name?, component?, redirect?, children?, meta?, beforeEnter? }`
- 路径可包含命名参数与可选正则:`/users/:id(\\d+)`、`/docs/:slug`
- 匹配时会将捕获到的参数解码并传递给组件的 `props.params`
示例:
```tsx
const routes = [
{ path: '/users/:id(\\d+)', component: UserDetail },
{ path: '/docs/:slug', component: DocDetail },
]
const UserDetail: FC<{ params: { id: string } }> = ({ params }) => 用户编号:{params.id}
```
## 命名路由与重定向
- 为路由记录声明 `name` 后,可通过 `router.push({ name, params })` 或 `RouterLink` 对象导航
- `redirect` 适合做默认子路由跳转,支持字符串路径、`{ path }`、`{ name, params }` 或函数返回这些对象
- 默认子路由常见写法是 `path: ''`
示例:
```tsx
const routes = [
{
path: '/guide/:section',
component: GuideLayout,
children: [
{ path: '', redirect: { name: 'guide-overview' } },
{ path: 'overview', name: 'guide-overview', component: GuideOverview },
{ path: ':topic', name: 'guide-topic', component: GuideTopic },
],
},
]
await router.push({ name: 'guide-topic', params: { section: 'router', topic: 'guards' } })
```
## 嵌套路由
- 子路由通过 `children` 定义,子路径默认相对父级路径拼接
- 父组件中的 `RouterView` 会继续渲染下一层命中的子路由
- 当前 `route.matched` 会包含从父到子的完整命中链
示例:
```tsx
import { type FC } from '@rue-js/rue'
import { RouterView } from '@rue-js/router'
const GuideLayout: FC<{ params: { section: string } }> = props => (
栏目:{props.params.section}
)
const GuideTopic: FC<{ params: { section: string; topic: string } }> = props => (
主题:{props.params.section}/{props.params.topic}
)
const routes = [
{
path: '/guide/:section',
component: GuideLayout,
children: [{ path: ':topic', component: GuideTopic }],
},
]
```
## 视图渲染:RouterView
- `RouterView` 会在固定的锚点区间内渲染当前匹配层级的组件
- 当无匹配时清空渲染区间;相同组件连续命中时避免重复渲染
- 匹配到的组件会收到 `{ params }` 作为 props
示例:
```tsx
const App: FC = () => (
)
```
## 链接导航:RouterLink
- `RouterLink` 渲染为 `` 元素,默认拦截左键点击并执行导航
- `to` 可传路径字符串,或 `{ path }`、`{ name, params }` 形式的对象;`replace` 为 `true` 时使用替换而非新增历史记录
- 其他传入的属性会透传给渲染的 ``(例如 `className`)
示例:
```tsx
查看文章
守卫章节
返回设置
```
## 运行时 API
- `useRouter()`:获取当前上下文的 Router(容器优先,其次为活动路由)
- `useRoute()`:获取当前路由匹配结果的信号(`SignalHandle`)
示例:
```tsx
import { useRoute } from '@rue-js/router'
const Current: FC = () => {
const route = useRoute()
return 当前路径:{route.get()?.path}
}
```
## 导航守卫
- `router.beforeEach()`:注册全局前置守卫
- `route.beforeEnter`:注册路由独享守卫
- `router.afterEach()`:注册导航结束后的后置钩子,第三个参数会拿到失败结果或错误对象
- 守卫可返回同步值或 Promise
- 守卫返回 `false` 会取消导航;返回字符串或 location 对象会重定向到新地址
- 可通过 `isNavigationFailure()` 与 `NavigationFailureType` 判断 `push/replace` 的失败结果
- 守卫直接 `throw` 或返回 rejected Promise 时,`push/replace` 会 reject,同一次导航的 `afterEach` 会收到该错误对象
示例:
```tsx
import {
createRouter,
createWebHashHistory,
isNavigationFailure,
NavigationFailureType,
} from '@rue-js/router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/login', component: LoginPage },
{
path: '/admin',
component: AdminPage,
meta: { requiresAuth: true },
beforeEnter: to => {
if (to?.meta.requiresAuth && !hasAdminRole()) {
return '/login'
}
},
},
],
})
router.beforeEach(to => {
if (to?.meta.requiresAuth && !isAuthenticated()) {
return { name: 'login' }
}
})
router.afterEach((to, from, failure) => {
console.log('navigated:', from?.path, '->', to?.path, failure)
})
const result = await router.push('/admin')
if (isNavigationFailure(result, NavigationFailureType.aborted)) {
console.log('navigation aborted')
}
```
## 在应用中使用
- 在应用入口创建并挂载,安装路由作为插件
示例应用入口:
```tsx
import { type FC, useApp } from '@rue-js/rue'
import { RouterView } from '@rue-js/router'
import router from './router'
import SiteLayout from './pages/site/components/Layout'
const RootApp: FC = () => (
)
useApp(RootApp).use(router).mount('#app')
```
## 最佳实践
- 路由优先级按定义顺序匹配,通用规则放在靠后位置
- 对需要约束格式的参数使用正则(如 `/orders/:id(\\d+)`)
- 使用 `replace` 避免在设置类页面产生过多历史栈记录
- 在组件中通过 `props.params` 读取参数,避免自行解析路径