v2.3.0 新增
useLink组合式 API,暴露 RouterLink 内部行为为组合式函数,用于构建自定义导航组件;同时新增isNavigationFailure工具函数,简化导航失败的类型检查。
前言
@meng-xi/uni-router 在 v2.2.0 完成了守卫系统的全面返回值模式改造,并新增了 onBeforeRouteLeave 组件内离开守卫。v2.3.0 在此基础上进一步扩展组合式 API 体系,新增 useLink 和 isNavigationFailure
两个实用功能,同时完成了 composables 目录的代码重构,提升可维护性。
一、新增能力:useLink 组合式 API
1. 问题分析
RouterLink 组件提供了声明式导航能力,但在以下场景中存在局限:
- 自定义导航组件:需要继承
RouterLink组件的能力但覆盖其 UI 渲染 - 响应式导航状态:需要获取
isActive、isExactActive等匹配状态用于条件渲染 - 非模板场景:在
setup()中需要编程式获取链接状态
// v2.2.x — 需要手动判断路由匹配
import { useRouter, useRoute } from '@meng-xi/uni-router'
import { computed } from 'vue'
const router = useRouter()
const route = useRoute()
// 手动判断
const isActive = computed(() => {
return route.value.path === router.resolve({ name: 'home' }).path
})
每次都需要手动 router.resolve() + currentRoute 比较,代码重复且容易出错。
2. useLink 组合式 API
useLink 将 RouterLink 的导航逻辑和匹配状态封装为组合式函数,与 Vue Router 4.x 的 useLink 行为一致。
类型
function useLink(options: UseLinkOptions): UseLinkReturn
参数
| 属性 | 类型 | 说明 |
|---|---|---|
to | RouteLocationRaw | 目标路由位置 |
replace? | boolean | 是否使用 replace 模式 |
relaunch? | boolean | 是否使用 relaunch 模式 |
返回值
| 属性 | 类型 | 说明 |
|---|---|---|
route | ComputedRef<RouteLocation> | 解析后的路由对象 |
href | ComputedRef<string> | 目标路径(fullPath) |
isActive | ComputedRef<boolean> | 当前路由是否匹配(比较 path) |
isExactActive | ComputedRef<boolean> | 是否完全匹配(比较 fullPath) |
navigate | () => Promise<NavigationResult> | 执行导航 |
基本用法
import { useLink } from '@meng-xi/uni-router'
const { href, isActive, isExactActive, navigate } = useLink({
to: { name: 'pagesDetailDetail', query: { id: '1' } }
})
console.log(href.value) // '/pages/detail/detail?id=1'
console.log(isActive.value) // 当前路由是否匹配此链接
自定义导航组件
<script setup lang="ts">
import { useLink } from '@meng-xi/uni-router'
import { computed } from 'vue'
const props = defineProps<{
to: RouteLocationRaw
replace?: boolean
activeClass?: string
}>()
const { href, isActive, navigate } = useLink(props)
const classes = computed(() => ({
'nav-link': true,
[props.activeClass || 'active']: isActive.value
}))
</script>
<template>
<view :class="classes" @click="navigate">
<slot />
</view>
</template>
响应式匹配
import { useLink } from '@meng-xi/uni-router'
import { computed } from 'vue'
const homeLink = useLink({ to: { name: 'pagesIndexIndex' } })
const aboutLink = useLink({ to: { name: 'pagesAboutAbout' } })
const currentTab = computed(() => {
if (homeLink.isActive.value) return 'home'
if (aboutLink.isActive.value) return 'about'
return null
})
3. 与 RouterLink 的关系
useLink 是 RouterLink 组件的内部实现。组件内部使用 useLink 获取导航状态,通过 useLink 可以在不依赖组件的情况下构建自定义导航逻辑。
二、新增能力:isNavigationFailure 工具函数
1. 问题分析
导航失败时,catch 块中需要手动进行 instanceof + code 双重检查:
// v2.2.x — 手动检查,代码冗长
try {
await router.push('/somewhere')
} catch (error) {
if (error instanceof NavigationFailure && error.code === RouterErrorCode.NAVIGATION_DUPLICATED) {
// 忽略重复导航
}
}
每次都需要写 error instanceof NavigationFailure && error.code === RouterErrorCode.XXX,代码重复且 TypeScript 类型收窄不明显。
2. isNavigationFailure 工具函数
isNavigationFailure 是 TypeScript 类型守卫,自动收窄类型,支持可选错误码参数。
类型
function isNavigationFailure(error: unknown, code?: RouterErrorCode): error is NavigationFailure
不传 code:仅检查是否为导航失败
import { isNavigationFailure } from '@meng-xi/uni-router'
try {
await router.push('/somewhere')
} catch (error) {
if (isNavigationFailure(error)) {
console.log('导航失败:', error.code)
}
}
传入 code:检查特定类型
import { isNavigationFailure, RouterErrorCode } from '@meng-xi/uni-router'
try {
await router.push('/somewhere')
} catch (error) {
// 重复导航 → 静默忽略
if (isNavigationFailure(error, RouterErrorCode.NAVIGATION_DUPLICATED)) return
// 守卫中止 → 无需处理
if (isNavigationFailure(error, RouterErrorCode.NAVIGATION_ABORTED)) return
// 其他错误 → 显示提示
uni.showToast({ title: '导航失败', icon: 'none' })
console.error(error)
}
与手动检查的对比
// 手动检查(繁琐)
if (error instanceof NavigationFailure && error.code === RouterErrorCode.NAVIGATION_DUPLICATED)
// 使用 isNavigationFailure(简洁)
if (isNavigationFailure(error, RouterErrorCode.NAVIGATION_DUPLICATED))
三、优化:composables 目录重构
1. 重构前
src/composables/index.ts 包含所有组合式函数的定义和导出,文件长达 89 行,包含 useRouter、useRoute、getReactiveRoute 三个函数和 onBeforeRouteLeave 的 re-export。
2. 重构后
src/composables/
├── index.ts # 纯导出文件(4 行)
├── router.ts # useRouter()
├── route.ts # useRoute() + getReactiveRoute()
├── link.ts # useLink()
└── guard/
└── route-leave.ts # onBeforeRouteLeave()
每个关注点独立文件,职责清晰,便于单元测试和后续扩展。
四、升级指南
v2.3.0 完全向后兼容,无需修改现有代码即可升级。
新增导出
// 组合式 API
export { useLink } from '@meng-xi/uni-router'
// 工具函数
export { isNavigationFailure } from '@meng-xi/uni-router'
// 类型
export type { UseLinkOptions, UseLinkReturn } from '@meng-xi/uni-router'
推荐迁移
- 将
error instanceof NavigationFailure && error.code === RouterErrorCode.XXX替换为isNavigationFailure(error, RouterErrorCode.XXX) - 在自定义导航组件中使用
useLink替代手动router.resolve()+currentRoute比较
版本兼容性
| 功能 | v2.2.x | v2.3.0 |
|---|---|---|
useLink 组合式 API | 不存在 | 新增 |
isNavigationFailure 工具函数 | 不存在 | 新增 |
UseLinkOptions / UseLinkReturn 类型 | 不存在 | 新增 |
useRouter / useRoute | 支持 | 支持(代码重构,外部无感知) |
| 守卫返回值模式 | 支持 | 支持 |
onBeforeRouteLeave | 支持 | 支持 |