uni-router-enhance 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +692 -0
- package/dist/index.d.mts +2977 -0
- package/dist/index.d.ts +2977 -0
- package/dist/index.js +477 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +439 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 SanshanStreet
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
# uni-router-enhance
|
|
2
|
+
|
|
3
|
+
一个为 uni-app 设计的**类型安全**路由增强库,提供完整的 TypeScript 类型支持、路由守卫、动态处理函数等高级特性。
|
|
4
|
+
|
|
5
|
+
## ✨ 特性
|
|
6
|
+
|
|
7
|
+
- 🔒 **完全类型安全** - 基于 `pages.json` 自动生成路由类型,避免路由拼写错误
|
|
8
|
+
- 🛡️ **导航守卫** - 支持 `beforeEach` 和 `afterEach` 全局守卫
|
|
9
|
+
- 🎯 **动态处理函数** - 为特定路由注册数据预加载、权限检查等处理逻辑
|
|
10
|
+
- 📦 **查询参数类型化** - 支持 TypeScript 类型推断的查询参数
|
|
11
|
+
- 🔄 **自动类型生成** - Vite 插件自动从 `pages.json` 生成路由类型
|
|
12
|
+
- 🎨 **灵活的页面关闭策略** - 支持 `navigateTo`、`redirectTo`、`reLaunch` 等多种跳转方式
|
|
13
|
+
- 💾 **路由数据缓存** - 自动缓存查询参数和处理函数返回值
|
|
14
|
+
|
|
15
|
+
## 📦 安装
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install uni-router-enhance
|
|
19
|
+
# 或
|
|
20
|
+
pnpm add uni-router-enhance
|
|
21
|
+
# 或
|
|
22
|
+
yarn add uni-router-enhance
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 🚀 快速开始
|
|
26
|
+
|
|
27
|
+
### 1. 配置 Vite 插件
|
|
28
|
+
|
|
29
|
+
在 `vite.config.ts` 中配置自动类型生成插件:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { defineConfig } from 'vite'
|
|
33
|
+
import uni from '@dcloudio/vite-plugin-uni'
|
|
34
|
+
import { routeTypesPlugin } from 'uni-router-enhance'
|
|
35
|
+
|
|
36
|
+
export default defineConfig({
|
|
37
|
+
plugins: [
|
|
38
|
+
uni(),
|
|
39
|
+
// 自动从 pages.json 生成路由类型
|
|
40
|
+
routeTypesPlugin('./types/auto-page.d.ts')
|
|
41
|
+
]
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 2. 创建 Router 实例
|
|
46
|
+
|
|
47
|
+
在 `src/router/index.ts` 中创建 router 实例:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { createRouter } from 'uni-router-enhance'
|
|
51
|
+
import pagesJson from '../pages.json'
|
|
52
|
+
import type { ENHANCE_ROUTE_PATH } from '../../types/auto-page.d.ts'
|
|
53
|
+
|
|
54
|
+
// 创建路由实例,传入 pages.json 配置
|
|
55
|
+
const router = createRouter<ENHANCE_ROUTE_PATH>(pagesJson)
|
|
56
|
+
|
|
57
|
+
// 配置全局前置守卫
|
|
58
|
+
router.beforeEach((to, from) => {
|
|
59
|
+
console.log('导航前:', to, from)
|
|
60
|
+
// 返回 false 可以取消导航
|
|
61
|
+
// 返回路由名称或路由对象可以重定向
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// 配置全局后置守卫
|
|
65
|
+
router.afterEach((to, from) => {
|
|
66
|
+
console.log('导航后:', to, from)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// 导出钩子函数供页面使用
|
|
70
|
+
const { useRouter, useRoute } = router
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
useRouter,
|
|
74
|
+
useRoute,
|
|
75
|
+
router
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 3. 在页面中使用
|
|
80
|
+
|
|
81
|
+
#### 基本路由跳转
|
|
82
|
+
|
|
83
|
+
```vue
|
|
84
|
+
<script setup lang="ts">
|
|
85
|
+
import { useRouter } from '@/router'
|
|
86
|
+
|
|
87
|
+
const { push } = useRouter()
|
|
88
|
+
|
|
89
|
+
// 简单跳转(类型安全)
|
|
90
|
+
const goToHome = () => {
|
|
91
|
+
push('home')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 带查询参数跳转
|
|
95
|
+
const goToDetail = () => {
|
|
96
|
+
push({
|
|
97
|
+
path: 'detail',
|
|
98
|
+
query: {
|
|
99
|
+
id: '123',
|
|
100
|
+
name: 'Product'
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 带回调的跳转
|
|
106
|
+
const goToProfile = () => {
|
|
107
|
+
push('profile', {
|
|
108
|
+
success: (result) => {
|
|
109
|
+
console.log('跳转成功,handler 返回:', result)
|
|
110
|
+
},
|
|
111
|
+
fail: (error) => {
|
|
112
|
+
console.error('跳转失败:', error)
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
</script>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### 页面关闭策略
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// 默认: navigateTo - 保留当前页面
|
|
123
|
+
push({
|
|
124
|
+
path: 'detail'
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// redirectTo - 关闭当前页面
|
|
128
|
+
push({
|
|
129
|
+
path: 'login'
|
|
130
|
+
close: 'current'
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// reLaunch - 关闭所有页面
|
|
134
|
+
push({
|
|
135
|
+
path: 'index',
|
|
136
|
+
close: 'all'
|
|
137
|
+
})
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### 获取当前路由信息
|
|
141
|
+
|
|
142
|
+
```vue
|
|
143
|
+
<script setup lang="ts">
|
|
144
|
+
import { useRoute } from '@/router'
|
|
145
|
+
|
|
146
|
+
const route = useRoute()
|
|
147
|
+
|
|
148
|
+
// 访问路由信息
|
|
149
|
+
console.log('当前路由名称:', route.name)
|
|
150
|
+
console.log('路由元信息:', route.meta)
|
|
151
|
+
console.log('查询参数:', route.query)
|
|
152
|
+
console.log('Handler 返回值:', route.handlerResult)
|
|
153
|
+
</script>
|
|
154
|
+
|
|
155
|
+
<template>
|
|
156
|
+
<view>
|
|
157
|
+
<text>当前页面: {{ route.name }}</text>
|
|
158
|
+
<text>参数 ID: {{ route.query.id }}</text>
|
|
159
|
+
</view>
|
|
160
|
+
</template>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## 🔧 高级功能
|
|
164
|
+
|
|
165
|
+
### 导航守卫
|
|
166
|
+
|
|
167
|
+
#### 全局前置守卫 (beforeEach)
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
router.beforeEach((to, from) => {
|
|
171
|
+
console.log(`从 ${from.name} 跳转到 ${to.name}`)
|
|
172
|
+
|
|
173
|
+
// 权限检查示例
|
|
174
|
+
if (to.meta?.requireAuth && !isLoggedIn()) {
|
|
175
|
+
// 重定向到登录页
|
|
176
|
+
return 'login'
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 返回 false 取消导航
|
|
180
|
+
if (someCondition) {
|
|
181
|
+
return false
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 不返回或返回 true 继续导航
|
|
185
|
+
})
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### 全局后置守卫 (afterEach)
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
router.afterEach((to, from) => {
|
|
192
|
+
// 页面访问统计
|
|
193
|
+
analytics.track('page_view', {
|
|
194
|
+
from: from.name,
|
|
195
|
+
to: to.name,
|
|
196
|
+
timestamp: Date.now()
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
// 设置页面标题
|
|
200
|
+
if (to.meta?.title) {
|
|
201
|
+
uni.setNavigationBarTitle({ title: to.meta.title })
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### 守卫返回值
|
|
207
|
+
|
|
208
|
+
- `undefined` 或 `true`: 继续导航
|
|
209
|
+
- `false`: 取消导航
|
|
210
|
+
- 路由名称字符串: 重定向到指定路由
|
|
211
|
+
- 路由对象: 重定向到指定路由并携带参数
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
router.beforeEach((to, from) => {
|
|
215
|
+
// 简单重定向
|
|
216
|
+
if (needRedirect) {
|
|
217
|
+
return 'home'
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// 带参数重定向
|
|
221
|
+
if (needRedirectWithParams) {
|
|
222
|
+
return {
|
|
223
|
+
path: 'detail',
|
|
224
|
+
query: { id: '123' }
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 路由处理函数 (Handler)
|
|
231
|
+
|
|
232
|
+
Handler 函数允许你在路由跳转时执行自定义逻辑,例如数据预加载、权限验证、埋点上报等。
|
|
233
|
+
|
|
234
|
+
#### 注册 Handler
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { router } from '@/router'
|
|
238
|
+
|
|
239
|
+
// 数据预加载示例
|
|
240
|
+
router.register('productDetail', async (payload) => {
|
|
241
|
+
const { query } = payload
|
|
242
|
+
const productId = query.id
|
|
243
|
+
|
|
244
|
+
// 预加载商品数据
|
|
245
|
+
const product = await fetchProduct(productId)
|
|
246
|
+
|
|
247
|
+
// 返回的数据可在目标页面通过 route.handlerResult 获取
|
|
248
|
+
return product
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
// 权限检查示例
|
|
252
|
+
router.register('adminPanel', async (payload) => {
|
|
253
|
+
const user = await getCurrentUser()
|
|
254
|
+
|
|
255
|
+
if (!user.isAdmin) {
|
|
256
|
+
throw new Error('无权限访问管理面板')
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return { allowed: true }
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
// 埋点上报示例
|
|
263
|
+
router.register('orderList', async (payload) => {
|
|
264
|
+
analytics.track('page_view', {
|
|
265
|
+
page: 'orderList',
|
|
266
|
+
timestamp: Date.now(),
|
|
267
|
+
...payload.query
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### 获取 Handler 返回值
|
|
273
|
+
|
|
274
|
+
```vue
|
|
275
|
+
<script setup lang="ts">
|
|
276
|
+
import { useRoute } from '@/router'
|
|
277
|
+
|
|
278
|
+
const route = useRoute()
|
|
279
|
+
|
|
280
|
+
// 获取 handler 返回的数据
|
|
281
|
+
const product = route.handlerResult as Product
|
|
282
|
+
|
|
283
|
+
onMounted(() => {
|
|
284
|
+
if (product) {
|
|
285
|
+
console.log('预加载的商品数据:', product)
|
|
286
|
+
}
|
|
287
|
+
})
|
|
288
|
+
</script>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
#### 动态管理 Handler
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// 检查是否已注册
|
|
295
|
+
if (router.has('productDetail')) {
|
|
296
|
+
console.log('已注册 productDetail handler')
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// 注销 handler(返回取消函数)
|
|
300
|
+
const unregister = router.register('temp', async () => {
|
|
301
|
+
// 临时逻辑
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
// 稍后移除
|
|
305
|
+
unregister()
|
|
306
|
+
|
|
307
|
+
// 或直接移除
|
|
308
|
+
router.unregister('temp')
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### 路由元信息 (Meta)
|
|
312
|
+
|
|
313
|
+
路由元信息从 `pages.json` 自动提取,包含以下字段:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
interface RouteMeta {
|
|
317
|
+
/** 页面路径 */
|
|
318
|
+
url: string
|
|
319
|
+
/** 页面标题 */
|
|
320
|
+
navigationBarTitleText?: string
|
|
321
|
+
/** 是否为 tabBar 页面 */
|
|
322
|
+
isTabBar?: boolean
|
|
323
|
+
/** 页面唯一标识 */
|
|
324
|
+
name: string
|
|
325
|
+
/** 页面样式配置 */
|
|
326
|
+
style?: Record<string, any>
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
在 `pages.json` 中配置的页面样式会自动映射到 meta:
|
|
331
|
+
|
|
332
|
+
```json
|
|
333
|
+
{
|
|
334
|
+
"pages": [
|
|
335
|
+
{
|
|
336
|
+
"path": "pages/home/index",
|
|
337
|
+
"style": {
|
|
338
|
+
"navigationBarTitleText": "首页",
|
|
339
|
+
"enablePullDownRefresh": true
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
访问元信息:
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
const route = useRoute()
|
|
350
|
+
console.log(route.meta?.navigationBarTitleText) // "首页"
|
|
351
|
+
console.log(route.meta?.isTabBar) // true/false
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## 📝 API 参考
|
|
355
|
+
|
|
356
|
+
### createRouter
|
|
357
|
+
|
|
358
|
+
创建 router 实例。
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
function createRouter<TName extends string>(
|
|
362
|
+
routes?: PagesConfig
|
|
363
|
+
): Router<TName>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**参数:**
|
|
367
|
+
- `routes`: pages.json 配置对象(可选)
|
|
368
|
+
|
|
369
|
+
**返回:** Router 实例
|
|
370
|
+
|
|
371
|
+
### Router 实例方法
|
|
372
|
+
|
|
373
|
+
#### register
|
|
374
|
+
|
|
375
|
+
注册路由处理函数。
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
register(name: TName, handler: RouteHandler): () => void
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**参数:**
|
|
382
|
+
- `name`: 路由名称
|
|
383
|
+
- `handler`: 处理函数,接收 payload,可返回任意值或 Promise
|
|
384
|
+
|
|
385
|
+
**返回:** 取消注册的函数
|
|
386
|
+
|
|
387
|
+
#### unregister
|
|
388
|
+
|
|
389
|
+
移除已注册的处理函数。
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
unregister(name: TName): boolean
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**返回:** 是否成功移除
|
|
396
|
+
|
|
397
|
+
#### has
|
|
398
|
+
|
|
399
|
+
检查是否已注册处理函数。
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
has(name: TName): boolean
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
#### beforeEach
|
|
406
|
+
|
|
407
|
+
添加全局前置守卫。
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
beforeEach(guard: NavigationGuard<TName>): () => void
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
**返回:** 移除守卫的函数
|
|
414
|
+
|
|
415
|
+
#### afterEach
|
|
416
|
+
|
|
417
|
+
添加全局后置守卫。
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
afterEach(guard: NavigationGuard<TName>): () => void
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**返回:** 移除守卫的函数
|
|
424
|
+
|
|
425
|
+
#### useRouter
|
|
426
|
+
|
|
427
|
+
返回路由操作钩子。
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
useRouter(): RouterHookResult<TName>
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**返回对象:**
|
|
434
|
+
- `push`: 类型安全的路由跳转函数
|
|
435
|
+
|
|
436
|
+
#### useRoute
|
|
437
|
+
|
|
438
|
+
返回当前路由信息。
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
useRoute(): RouteInfo<TName>
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
**返回对象:**
|
|
445
|
+
- `name`: 当前路由名称
|
|
446
|
+
- `meta`: 路由元信息
|
|
447
|
+
- `query`: 查询参数
|
|
448
|
+
- `handlerResult`: Handler 返回值
|
|
449
|
+
|
|
450
|
+
### useRouter 返回的方法
|
|
451
|
+
|
|
452
|
+
#### push
|
|
453
|
+
|
|
454
|
+
类型安全的路由跳转。
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
push(
|
|
458
|
+
data: TName | RouterParams<TName>,
|
|
459
|
+
callbacks?: {
|
|
460
|
+
success?: (result?: unknown) => void
|
|
461
|
+
fail?: (error?: any) => void
|
|
462
|
+
}
|
|
463
|
+
): Promise<void>
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
**参数:**
|
|
467
|
+
- `data`: 路由名称字符串或路由参数对象
|
|
468
|
+
- `callbacks`: 可选的成功/失败回调
|
|
469
|
+
|
|
470
|
+
**RouterParams 对象:**
|
|
471
|
+
```typescript
|
|
472
|
+
interface RouterParams<TPath extends string> {
|
|
473
|
+
path?: TPath // 路由名称
|
|
474
|
+
query?: Record<string, any> // 查询参数
|
|
475
|
+
close?: CloseTypes // 页面关闭策略
|
|
476
|
+
success?: (result?: unknown) => void // 成功回调
|
|
477
|
+
fail?: (error?: any) => void // 失败回调
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### CloseTypes 枚举
|
|
482
|
+
|
|
483
|
+
页面关闭策略。
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
enum CloseTypes {
|
|
487
|
+
default = 'default', // navigateTo - 保留当前页面
|
|
488
|
+
current = 'current', // redirectTo - 关闭当前页面
|
|
489
|
+
all = 'all' // reLaunch - 关闭所有页面
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### routeTypesPlugin
|
|
494
|
+
|
|
495
|
+
Vite 插件,自动生成路由类型。
|
|
496
|
+
|
|
497
|
+
```typescript
|
|
498
|
+
function routeTypesPlugin(dts: string): Plugin
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
**参数:**
|
|
502
|
+
- `dts`: 类型文件输出路径
|
|
503
|
+
|
|
504
|
+
**示例:**
|
|
505
|
+
```typescript
|
|
506
|
+
routeTypesPlugin('./types/auto-page.d.ts')
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
生成的类型文件示例:
|
|
510
|
+
|
|
511
|
+
```typescript
|
|
512
|
+
export type ENHANCE_ROUTE_PATH =
|
|
513
|
+
| 'demo'
|
|
514
|
+
| 'home'
|
|
515
|
+
| 'index'
|
|
516
|
+
| 'profile'
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
## 🎯 完整示例
|
|
520
|
+
|
|
521
|
+
### 示例 1:电商应用
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
// router/index.ts
|
|
525
|
+
import { createRouter } from 'uni-router-enhance'
|
|
526
|
+
import pagesJson from '../pages.json'
|
|
527
|
+
import type { ENHANCE_ROUTE_PATH } from '../../types/auto-page.d.ts'
|
|
528
|
+
|
|
529
|
+
const router = createRouter<ENHANCE_ROUTE_PATH>(pagesJson)
|
|
530
|
+
|
|
531
|
+
// 全局登录检查
|
|
532
|
+
router.beforeEach((to, from) => {
|
|
533
|
+
const needAuth = ['profile', 'order', 'cart'].includes(to.name)
|
|
534
|
+
|
|
535
|
+
if (needAuth && !isLoggedIn()) {
|
|
536
|
+
uni.showToast({ title: '请先登录', icon: 'none' })
|
|
537
|
+
return 'login'
|
|
538
|
+
}
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
// 商品详情页数据预加载
|
|
542
|
+
router.register('productDetail', async (payload) => {
|
|
543
|
+
const { query } = payload
|
|
544
|
+
const product = await fetchProduct(query.id)
|
|
545
|
+
return product
|
|
546
|
+
})
|
|
547
|
+
|
|
548
|
+
// 订单列表页权限检查
|
|
549
|
+
router.register('orderList', async () => {
|
|
550
|
+
const user = await getCurrentUser()
|
|
551
|
+
if (!user.hasOrders) {
|
|
552
|
+
throw new Error('暂无订单权限')
|
|
553
|
+
}
|
|
554
|
+
})
|
|
555
|
+
|
|
556
|
+
export const { useRouter, useRoute } = router
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### 示例 2:商品详情页
|
|
560
|
+
|
|
561
|
+
```vue
|
|
562
|
+
<script setup lang="ts">
|
|
563
|
+
import { useRoute } from '@/router'
|
|
564
|
+
|
|
565
|
+
// 获取路由信息
|
|
566
|
+
const route = useRoute()
|
|
567
|
+
|
|
568
|
+
// 从 handler 获取预加载的数据
|
|
569
|
+
const product = computed(() => route.handlerResult as Product)
|
|
570
|
+
|
|
571
|
+
onLoad(() => {
|
|
572
|
+
console.log('页面参数:', route.query)
|
|
573
|
+
console.log('预加载数据:', product.value)
|
|
574
|
+
})
|
|
575
|
+
</script>
|
|
576
|
+
|
|
577
|
+
<template>
|
|
578
|
+
<view class="product-detail">
|
|
579
|
+
<image :src="product?.image" />
|
|
580
|
+
<text>{{ product?.name }}</text>
|
|
581
|
+
<text>¥{{ product?.price }}</text>
|
|
582
|
+
</view>
|
|
583
|
+
</template>
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### 示例 3:商品列表页
|
|
587
|
+
|
|
588
|
+
```vue
|
|
589
|
+
<script setup lang="ts">
|
|
590
|
+
import { useRouter } from '@/router'
|
|
591
|
+
|
|
592
|
+
const { push } = useRouter()
|
|
593
|
+
|
|
594
|
+
const products = ref([])
|
|
595
|
+
|
|
596
|
+
const goToDetail = (productId: string) => {
|
|
597
|
+
push({
|
|
598
|
+
path: 'productDetail',
|
|
599
|
+
query: { id: productId },
|
|
600
|
+
success: () => {
|
|
601
|
+
console.log('跳转成功')
|
|
602
|
+
},
|
|
603
|
+
fail: (error) => {
|
|
604
|
+
uni.showToast({ title: error.message, icon: 'none' })
|
|
605
|
+
}
|
|
606
|
+
})
|
|
607
|
+
}
|
|
608
|
+
</script>
|
|
609
|
+
|
|
610
|
+
<template>
|
|
611
|
+
<view>
|
|
612
|
+
<view
|
|
613
|
+
v-for="item in products"
|
|
614
|
+
:key="item.id"
|
|
615
|
+
@click="goToDetail(item.id)"
|
|
616
|
+
>
|
|
617
|
+
{{ item.name }}
|
|
618
|
+
</view>
|
|
619
|
+
</view>
|
|
620
|
+
</template>
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
## 🔍 TypeScript 支持
|
|
624
|
+
|
|
625
|
+
本库完全使用 TypeScript 编写,提供完整的类型定义。
|
|
626
|
+
|
|
627
|
+
### 自动类型推断
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
// ✅ 类型安全 - 路由名称会被自动检查
|
|
631
|
+
push('home')
|
|
632
|
+
|
|
633
|
+
// ❌ 类型错误 - 不存在的路由
|
|
634
|
+
push('nonexistent')
|
|
635
|
+
|
|
636
|
+
// ✅ 查询参数类型安全
|
|
637
|
+
push({
|
|
638
|
+
path: 'detail',
|
|
639
|
+
query: {
|
|
640
|
+
id: '123',
|
|
641
|
+
tab: 'info'
|
|
642
|
+
}
|
|
643
|
+
})
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
### 自定义类型
|
|
647
|
+
|
|
648
|
+
```typescript
|
|
649
|
+
// 定义查询参数类型
|
|
650
|
+
interface ProductDetailQuery {
|
|
651
|
+
id: string
|
|
652
|
+
from?: 'list' | 'search'
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// 在 handler 中使用
|
|
656
|
+
router.register('productDetail', async (payload) => {
|
|
657
|
+
const query = payload.query as ProductDetailQuery
|
|
658
|
+
console.log(query.id, query.from)
|
|
659
|
+
})
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
## ⚠️ 注意事项
|
|
663
|
+
|
|
664
|
+
1. **TabBar 页面限制**
|
|
665
|
+
- TabBar 页面只能使用 `uni.switchTab` 跳转
|
|
666
|
+
- 跳转到 TabBar 页面时,query 参数会被忽略
|
|
667
|
+
|
|
668
|
+
2. **路由名称提取规则**
|
|
669
|
+
- 路由名称从页面路径的第二段提取
|
|
670
|
+
- 例如:`pages/home/index` → 路由名称为 `home`
|
|
671
|
+
- 分包路径:`subpackage/detail/index` → 路由名称为 `detail`
|
|
672
|
+
|
|
673
|
+
3. **Handler 执行时机**
|
|
674
|
+
- Handler 在导航守卫之后、页面跳转之前执行
|
|
675
|
+
- Handler 抛出错误会阻止页面跳转
|
|
676
|
+
- Handler 返回 `false` 会取消导航
|
|
677
|
+
|
|
678
|
+
4. **缓存清理**
|
|
679
|
+
- 页面缓存在跳转失败时会自动清理
|
|
680
|
+
- 建议在页面 `onLoad` 后及时获取缓存数据
|
|
681
|
+
|
|
682
|
+
## 🤝 贡献
|
|
683
|
+
|
|
684
|
+
欢迎提交 Issue 和 Pull Request!
|
|
685
|
+
|
|
686
|
+
## 📄 License
|
|
687
|
+
|
|
688
|
+
MIT License
|
|
689
|
+
|
|
690
|
+
---
|
|
691
|
+
|
|
692
|
+
**Made with ❤️ for uni-app developers**
|