@feoe/fs-router 提供了强大的导航 Hook,让你能够在 React 组件中进行类型安全的路由导航。
useNavigation
是 @feoe/fs-router 的核心导航 Hook,提供了完整的路由导航功能,包括历史记录操作、路由跳转和 URL 构建等。
1import { useNavigation } from '@feoe/fs-router'
2
3function MyComponent() {
4 const navigation = useNavigation()
5
6 return (
7 <div>
8 <button onClick={() => navigation.back()}>返回</button>
9 <button onClick={() => navigation.forward()}>前进</button>
10 <button onClick={() => navigation.reload()}>刷新</button>
11 </div>
12 )
13}
返回到浏览器历史记录的上一页。
1const navigation = useNavigation()
2
3// 返回上一页
4navigation.back()
前进到浏览器历史记录的下一页。
1const navigation = useNavigation()
2
3// 前进到下一页
4navigation.forward()
重新加载当前页面。
1const navigation = useNavigation()
2
3// 刷新当前页面
4navigation.reload()
导航到指定路由,会在历史记录中添加新条目。
1const navigation = useNavigation()
2
3// 基本导航
4navigation.push('/users')
5
6// 带参数的导航
7navigation.push('/users/:id', { id: '123' })
8
9// 带查询参数的导航
10navigation.push('/users', undefined, { page: '1', size: '10' })
替换当前路由,不会在历史记录中添加新条目。
1const navigation = useNavigation()
2
3// 替换当前路由
4navigation.replace('/login')
5
6// 带参数替换
7navigation.replace('/users/:id', { id: '456' })
构建指定路由的 URL 字符串,不执行导航。
1const navigation = useNavigation()
2
3// 构建简单 URL
4const userListUrl = navigation.buildHref('/users')
5
6// 构建带参数的 URL
7const userDetailUrl = navigation.buildHref('/users/:id', { id: '123' })
8
9// 构建带查询参数的 URL
10const searchUrl = navigation.buildHref('/search', undefined, { q: 'react' })
1import { useNavigation } from '@feoe/fs-router'
2
3function NavigationControls() {
4 const navigation = useNavigation()
5
6 return (
7 <div className="navigation-controls">
8 <button
9 onClick={() => navigation.back()}
10 className="nav-button"
11 >
12 ← 返回
13 </button>
14
15 <button
16 onClick={() => navigation.forward()}
17 className="nav-button"
18 >
19 前进 →
20 </button>
21
22 <button
23 onClick={() => navigation.reload()}
24 className="nav-button"
25 >
26 🔄 刷新页面
27 </button>
28 </div>
29 )
30}
1import { useNavigation } from '@feoe/fs-router'
2
3function UserProfile() {
4 const navigation = useNavigation()
5
6 const handleCancel = () => {
7 // 取消编辑时返回上一页
8 navigation.back()
9 }
10
11 const handleSaveSuccess = () => {
12 // 保存成功后刷新页面显示最新数据
13 navigation.reload()
14 }
15
16 const handleError = () => {
17 // 发生错误时重新加载页面
18 navigation.reload()
19 }
20
21 return (
22 <div>
23 <h1>用户资料</h1>
24 <button onClick={handleCancel}>取消</button>
25 <button onClick={handleSaveSuccess}>保存</button>
26 </div>
27 )
28}
1import { useNavigation } from '@feoe/fs-router'
2
3function ProductList() {
4 const navigation = useNavigation()
5
6 const handleViewProduct = (productId: string) => {
7 // 导航到产品详情页,添加到历史记录
8 navigation.push('/products/:id', { id: productId })
9 }
10
11 const handleEditProduct = (productId: string) => {
12 // 导航到编辑页面,带查询参数
13 navigation.push('/products/:id/edit', { id: productId }, { mode: 'edit' })
14 }
15
16 const handleLogin = () => {
17 // 替换当前页面为登录页,不添加到历史记录
18 navigation.replace('/login')
19 }
20
21 return (
22 <div>
23 <button onClick={() => handleViewProduct('123')}>查看产品</button>
24 <button onClick={() => handleEditProduct('123')}>编辑产品</button>
25 <button onClick={handleLogin}>登录</button>
26 </div>
27 )
28}
1import { useNavigation } from '@feoe/fs-router'
2
3function LinkGenerator() {
4 const navigation = useNavigation()
5
6 // 构建各种类型的 URL
7 const homeUrl = navigation.buildHref('/')
8 const userProfileUrl = navigation.buildHref('/users/:id', { id: '456' })
9 const searchUrl = navigation.buildHref('/search', undefined, {
10 q: 'react',
11 category: 'frontend'
12 })
13
14 return (
15 <div>
16 <a href={homeUrl}>首页</a>
17 <a href={userProfileUrl}>用户资料</a>
18 <a href={searchUrl}>搜索结果</a>
19 </div>
20 )
21}
1import { useNavigation } from '@feoe/fs-router'
2
3function ShoppingCart() {
4 const navigation = useNavigation()
5
6 const handleCheckout = () => {
7 // 导航到结账页面,带购物车信息
8 navigation.push('/checkout', undefined, {
9 from: 'cart',
10 items: '3'
11 })
12 }
13
14 const handleContinueShopping = () => {
15 // 返回到商品列表,保持筛选条件
16 navigation.push('/products', undefined, {
17 category: 'electronics',
18 sort: 'price'
19 })
20 }
21
22 const handleEmptyCart = () => {
23 // 清空购物车后刷新页面
24 // 这里可以先清空数据,然后刷新
25 navigation.reload()
26 }
27
28 return (
29 <div>
30 <button onClick={handleCheckout}>去结账</button>
31 <button onClick={handleContinueShopping}>继续购物</button>
32 <button onClick={handleEmptyCart}>清空购物车</button>
33 </div>
34 )
35}
@feoe/fs-router 提供了完整的 TypeScript 支持,通过自动生成的 RouteTypes
接口确保路由导航的类型安全。
RouteTypes
接口会根据你的路由文件结构自动生成,包含所有可用的路由路径:
1// 自动生成的类型文件 (routes-type.ts)
2declare module "@feoe/fs-router" {
3 interface RouteTypes {
4 "/": {};
5 "/users": {};
6 "/users/:id": {};
7 "/products/:id": {};
8 "/products/:id/edit": {};
9 "/search": {};
10 "/checkout": {};
11 }
12}
使用 useNavigation
时,所有的路由路径都会有完整的类型检查:
1import { useNavigation } from '@feoe/fs-router'
2
3function TypeSafeNavigation() {
4 const navigation = useNavigation()
5
6 // ✅ 正确:路径存在于 RouteTypes 中
7 navigation.push('/users')
8 navigation.push('/users/:id', { id: '123' })
9
10 // ❌ 错误:TypeScript 会报错,路径不存在
11 // navigation.push('/invalid-path')
12
13 // ✅ 正确:参数类型检查
14 navigation.push('/products/:id', { id: '456' })
15
16 // ❌ 错误:缺少必需的参数
17 // navigation.push('/products/:id')
18
19 return <div>类型安全的导航示例</div>
20}
@feoe/fs-router 会自动推导路由参数的类型,确保参数的正确性:
1import { useNavigation } from '@feoe/fs-router'
2
3function ParameterTypeInference() {
4 const navigation = useNavigation()
5
6 // 自动推导参数类型
7 const handleUserNavigation = (userId: string) => {
8 // id 参数是必需的,类型为 string | number | boolean
9 navigation.push('/users/:id', { id: userId })
10 }
11
12 const handleProductNavigation = (productId: number) => {
13 // 支持多种参数类型
14 navigation.push('/products/:id', { id: productId })
15 }
16
17 const handleOptionalParams = () => {
18 // 可选参数的处理
19 navigation.push('/search/:category?', { category: 'electronics' })
20 // 或者不传递可选参数
21 navigation.push('/search/:category?')
22 }
23
24 return (
25 <div>
26 <button onClick={() => handleUserNavigation('123')}>
27 查看用户
28 </button>
29 <button onClick={() => handleProductNavigation(456)}>
30 查看产品
31 </button>
32 <button onClick={handleOptionalParams}>
33 搜索
34 </button>
35 </div>
36 )
37}
buildHref
方法同样支持完整的类型检查:
1import { useNavigation } from '@feoe/fs-router'
2
3function TypeSafeUrlBuilder() {
4 const navigation = useNavigation()
5
6 // 类型安全的 URL 构建
7 const buildUrls = () => {
8 // ✅ 正确:所有参数都有类型检查
9 const userUrl = navigation.buildHref('/users/:id', { id: '123' })
10 const productUrl = navigation.buildHref('/products/:id', { id: 456 })
11 const searchUrl = navigation.buildHref('/search', undefined, {
12 q: 'react',
13 page: '1'
14 })
15
16 return { userUrl, productUrl, searchUrl }
17 }
18
19 const urls = buildUrls()
20
21 return (
22 <div>
23 <p>用户链接: {urls.userUrl}</p>
24 <p>产品链接: {urls.productUrl}</p>
25 <p>搜索链接: {urls.searchUrl}</p>
26 </div>
27 )
28}
@feoe/fs-router 支持必需参数和可选参数的处理:
1import { useNavigation } from '@feoe/fs-router'
2
3function ParameterHandling() {
4 const navigation = useNavigation()
5
6 // 必需参数
7 const handleRequiredParams = () => {
8 // 必须提供 id 参数
9 navigation.push('/users/:id', { id: '123' })
10 navigation.push('/posts/:userId/:postId', {
11 userId: '456',
12 postId: '789'
13 })
14 }
15
16 // 可选参数
17 const handleOptionalParams = () => {
18 // 可选参数可以不提供
19 navigation.push('/search/:category?')
20 // 或者提供可选参数
21 navigation.push('/search/:category?', { category: 'tech' })
22 }
23
24 // 混合参数
25 const handleMixedParams = () => {
26 // 必需参数 + 可选参数
27 navigation.push('/users/:id/posts/:postId?', {
28 id: '123',
29 postId: '456' // 可选
30 })
31 }
32
33 return (
34 <div>
35 <button onClick={handleRequiredParams}>必需参数</button>
36 <button onClick={handleOptionalParams}>可选参数</button>
37 <button onClick={handleMixedParams}>混合参数</button>
38 </div>
39 )
40}
查询参数通过第三个参数传递,支持任意键值对:
1import { useNavigation } from '@feoe/fs-router'
2
3function QueryParameters() {
4 const navigation = useNavigation()
5
6 const handleSearch = () => {
7 // 基本查询参数
8 navigation.push('/search', undefined, {
9 q: 'react',
10 page: '1',
11 size: '10'
12 })
13 }
14
15 const handleFilter = () => {
16 // 复杂查询参数
17 navigation.push('/products', undefined, {
18 category: 'electronics',
19 brand: 'apple',
20 minPrice: '100',
21 maxPrice: '1000',
22 sort: 'price-asc',
23 inStock: 'true'
24 })
25 }
26
27 const handlePagination = (page: number, size: number) => {
28 // 动态查询参数
29 navigation.push('/users', undefined, {
30 page: page.toString(),
31 size: size.toString(),
32 sort: 'created_at',
33 order: 'desc'
34 })
35 }
36
37 return (
38 <div>
39 <button onClick={handleSearch}>搜索查询</button>
40 <button onClick={handleFilter}>产品筛选</button>
41 <button onClick={() => handlePagination(2, 20)}>分页导航</button>
42 </div>
43 )
44}
结合路由参数和查询参数的复杂使用场景:
1import { useNavigation } from '@feoe/fs-router'
2
3function ComplexRouting() {
4 const navigation = useNavigation()
5
6 const handleUserProfile = (userId: string, tab?: string) => {
7 // 路由参数 + 可选查询参数
8 const query = tab ? { tab } : undefined
9 navigation.push('/users/:id', { id: userId }, query)
10 }
11
12 const handleProductSearch = (category: string, filters: Record<string, string>) => {
13 // 路由参数 + 多个查询参数
14 navigation.push('/categories/:category', { category }, filters)
15 }
16
17 const handleDashboard = (userId: string, options: {
18 view?: 'grid' | 'list'
19 period?: 'day' | 'week' | 'month'
20 refresh?: boolean
21 }) => {
22 // 复杂的参数组合
23 const query: Record<string, string> = {}
24 if (options.view) query.view = options.view
25 if (options.period) query.period = options.period
26 if (options.refresh) query.refresh = 'true'
27
28 navigation.push('/dashboard/:userId', { userId }, query)
29 }
30
31 const buildComplexUrl = () => {
32 // 构建复杂 URL 而不导航
33 const url = navigation.buildHref(
34 '/admin/users/:id/settings/:section?',
35 { id: '123', section: 'profile' },
36 {
37 edit: 'true',
38 returnTo: '/admin/users',
39 timestamp: Date.now().toString()
40 }
41 )
42
43 console.log('构建的 URL:', url)
44 return url
45 }
46
47 return (
48 <div>
49 <button onClick={() => handleUserProfile('123', 'settings')}>
50 用户资料
51 </button>
52 <button onClick={() => handleProductSearch('electronics', {
53 brand: 'apple',
54 price: '100-1000'
55 })}>
56 产品搜索
57 </button>
58 <button onClick={() => handleDashboard('456', {
59 view: 'grid',
60 period: 'week'
61 })}>
62 仪表板
63 </button>
64 <button onClick={buildComplexUrl}>
65 构建复杂 URL
66 </button>
67 </div>
68 )
69}
1import { useNavigation } from '@feoe/fs-router'
2
3function BestPractices() {
4 const navigation = useNavigation()
5
6 const handleSafeNavigation = (id: string | number) => {
7 // 参数验证
8 if (!id) {
9 console.error('ID 参数不能为空')
10 return
11 }
12
13 // 类型转换
14 const safeId = typeof id === 'number' ? id.toString() : id
15 navigation.push('/users/:id', { id: safeId })
16 }
17
18 const handleConditionalNavigation = (user: { id: string; role: string }) => {
19 // 条件导航
20 const basePath = user.role === 'admin' ? '/admin/users/:id' : '/users/:id'
21 const query = user.role === 'admin' ? { admin: 'true' } : undefined
22
23 navigation.push(basePath, { id: user.id }, query)
24 }
25
26 return (
27 <div>
28 <button onClick={() => handleSafeNavigation('123')}>
29 安全导航
30 </button>
31 <button onClick={() => handleConditionalNavigation({
32 id: '456',
33 role: 'admin'
34 })}>
35 条件导航
36 </button>
37 </div>
38 )
39}