Hook API

@feoe/fs-router 提供了强大的导航 Hook,让你能够在 React 组件中进行类型安全的路由导航。

useNavigation

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}

API 方法

back()

返回到浏览器历史记录的上一页。

1const navigation = useNavigation()
2
3// 返回上一页
4navigation.back()

forward()

前进到浏览器历史记录的下一页。

1const navigation = useNavigation()
2
3// 前进到下一页
4navigation.forward()

reload()

重新加载当前页面。

1const navigation = useNavigation()
2
3// 刷新当前页面
4navigation.reload()

push(path, params?, query?)

导航到指定路由,会在历史记录中添加新条目。

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' })

replace(path, params?, query?)

替换当前路由,不会在历史记录中添加新条目。

1const navigation = useNavigation()
2
3// 替换当前路由
4navigation.replace('/login')
5
6// 带参数替换
7navigation.replace('/users/:id', { id: '456' })

buildHref(path, params?, query?)

构建指定路由的 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}

URL 构建示例

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 接口

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}

类型安全的 URL 构建

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}