uni-router v2.3.0 新增 useLink 和导航失败检查

0 阅读4分钟

v2.3.0 新增 useLink 组合式 API,暴露 RouterLink 内部行为为组合式函数,用于构建自定义导航组件;同时新增 isNavigationFailure 工具函数,简化导航失败的类型检查。

前言

@meng-xi/uni-router 在 v2.2.0 完成了守卫系统的全面返回值模式改造,并新增了 onBeforeRouteLeave 组件内离开守卫。v2.3.0 在此基础上进一步扩展组合式 API 体系,新增 useLinkisNavigationFailure 两个实用功能,同时完成了 composables 目录的代码重构,提升可维护性。


一、新增能力:useLink 组合式 API

1. 问题分析

RouterLink 组件提供了声明式导航能力,但在以下场景中存在局限:

  • 自定义导航组件:需要继承 RouterLink 组件的能力但覆盖其 UI 渲染
  • 响应式导航状态:需要获取 isActiveisExactActive 等匹配状态用于条件渲染
  • 非模板场景:在 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

参数

属性类型说明
toRouteLocationRaw目标路由位置
replace?boolean是否使用 replace 模式
relaunch?boolean是否使用 relaunch 模式

返回值

属性类型说明
routeComputedRef<RouteLocation>解析后的路由对象
hrefComputedRef<string>目标路径(fullPath)
isActiveComputedRef<boolean>当前路由是否匹配(比较 path)
isExactActiveComputedRef<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 的关系

useLinkRouterLink 组件的内部实现。组件内部使用 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 行,包含 useRouteruseRoutegetReactiveRoute 三个函数和 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.xv2.3.0
useLink 组合式 API不存在新增
isNavigationFailure 工具函数不存在新增
UseLinkOptions / UseLinkReturn 类型不存在新增
useRouter / useRoute支持支持(代码重构,外部无感知)
守卫返回值模式支持支持
onBeforeRouteLeave支持支持