等待导航结果 | Vue Router
理解导航生命周期
在 Vue Router 中,导航是异步操作。了解如何正确处理导航结果对于构建健壮的应用至关重要。导航可能因为多种原因被阻止或重定向,我们需要能够检测和处理这些情况。
导航可能被阻止的情况
- 用户已在目标页面 - 尝试导航到当前所在页面
- 导航守卫中断 - 守卫中返回
false - 新导航覆盖 - 在当前导航完成前发起新导航
- 重定向发生 - 守卫返回新位置进行重定向
- 错误抛出 - 导航过程中发生错误
检测导航结果
基本模式
javascript
// 发起导航并等待结果
const navigationResult = await router.push('/my-profile')
if (navigationResult) {
// 导航被阻止 - navigationResult 是一个 Navigation Failure 对象
console.log('导航被阻止:', navigationResult)
} else {
// 导航成功完成
console.log('导航成功')
this.isMenuOpen = false
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
实际应用示例
javascript
async function navigateToProfile() {
try {
const result = await router.push('/profile')
if (result) {
// 处理导航被阻止的情况
handleNavigationFailure(result)
} else {
// 导航成功,执行后续操作
closeMobileMenu()
showSuccessNotification('页面加载成功')
}
} catch (error) {
// 处理导航错误
console.error('导航错误:', error)
showErrorNotification('页面加载失败')
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
导航失败类型
Vue Router 提供了专门的工具来识别不同类型的导航失败:
导入工具函数
javascript
import {
NavigationFailureType,
isNavigationFailure
} from 'vue-router'1
2
3
4
2
3
4
检测特定类型的失败
javascript
const failure = await router.push('/some-route')
if (isNavigationFailure(failure, NavigationFailureType.aborted)) {
// 导航被守卫中止
showToast('您有未保存的更改,确定要离开吗?')
} else if (isNavigationFailure(failure, NavigationFailureType.cancelled)) {
// 导航被新导航取消
console.log('导航被新请求取消')
} else if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
// 尝试导航到当前页面
console.log('您已经在目标页面')
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
简化检测
javascript
// 只检查是否是导航失败(不关心具体类型)
if (isNavigationFailure(failure)) {
console.log('这是一个导航失败')
}1
2
3
4
2
3
4
导航失败类型详解
1. aborted - 被中止的导航
触发条件:导航守卫中返回 false
javascript
router.beforeEach((to, from) => {
if (hasUnsavedChanges() && !confirm('确定要离开吗?')) {
return false // 这会触发 aborted 失败
}
})
// 检测
const result = await router.push('/other-page')
if (isNavigationFailure(result, NavigationFailureType.aborted)) {
showUnsavedChangesWarning()
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
2. cancelled - 被取消的导航
触发条件:在当前导航完成前发起新的导航
javascript
// 快速连续发起多个导航
router.push('/page1')
const result = await router.push('/page2') // 这会取消前一个导航
if (isNavigationFailure(result, NavigationFailureType.cancelled)) {
console.log('前一个导航被取消')
}1
2
3
4
5
6
7
2
3
4
5
6
7
3. duplicated - 重复导航
触发条件:尝试导航到当前所在页面
javascript
// 当前已在 /home 页面
const result = await router.push('/home')
if (isNavigationFailure(result, NavigationFailureType.duplicated)) {
console.log('重复导航到当前页面')
}1
2
3
4
5
6
2
3
4
5
6
导航失败对象的属性
所有导航失败对象都包含有用的信息:
javascript
const failure = await router.push('/admin')
if (failure) {
console.log('失败信息:', {
type: failure.type, // 失败类型
from: failure.from.path, // 来源路径
to: failure.to.path, // 目标路径
message: failure.message // 错误消息
})
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
全局导航失败处理
使用 afterEach 守卫
javascript
router.afterEach((to, from, failure) => {
if (failure) {
// 发送错误报告到分析服务
sendToAnalytics(to, from, failure)
// 根据失败类型采取不同行动
switch (failure.type) {
case NavigationFailureType.aborted:
trackUserAbortedNavigation()
break
case NavigationFailureType.cancelled:
trackRapidNavigation()
break
case NavigationFailureType.duplicated:
trackRedundantNavigation()
break
}
}
})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
错误监控集成
javascript
router.afterEach((to, from, failure) => {
if (failure) {
// 集成错误监控系统
Sentry.captureException(new Error(`Navigation failed: ${failure.type}`), {
extra: {
from: from.path,
to: to.path,
failureType: failure.type
}
})
}
})1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
检测重定向
重定向不会导致导航失败,但需要特殊处理:
检测重定向发生
javascript
const result = await router.push('/my-profile')
// 检查是否发生了重定向
if (router.currentRoute.value.redirectedFrom) {
console.log('发生了重定向:', {
from: router.currentRoute.value.redirectedFrom.path,
to: router.currentRoute.value.path
})
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
重定向示例
javascript
// 导航守卫中的重定向
router.beforeEach((to, from) => {
if (to.path === '/admin' && !isAdmin()) {
return '/access-denied' // 这会触发重定向
}
})
// 使用
await router.push('/admin')
if (router.currentRoute.value.redirectedFrom) {
console.log('从管理员页面重定向而来')
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
实际应用场景
场景 1:移动端菜单管理
javascript
async function handleMobileNavigation(path) {
// 发起导航
const result = await router.push(path)
if (!result) {
// 只有真正离开当前页面时才关闭菜单
closeMobileMenu()
}
// 如果导航被阻止,菜单保持打开状态
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
场景 2:表单数据保护
javascript
const unsavedChanges = ref(false)
router.beforeEach((to, from) => {
if (unsavedChanges.value && !confirm('有未保存的更改,确定要离开吗?')) {
return false
}
})
async function saveAndNavigate(path) {
try {
await saveFormData()
const result = await router.push(path)
if (result && isNavigationFailure(result, NavigationFailureType.aborted)) {
// 用户选择不离开,保持表单状态
console.log('用户选择留在当前页面')
}
} catch (error) {
console.error('保存失败:', error)
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
场景 3:导航进度指示
javascript
const isLoading = ref(false)
async function navigateWithLoading(path) {
isLoading.value = true
try {
const result = await router.push(path)
if (result) {
// 导航被阻止,不需要隐藏加载指示器
console.log('导航被阻止')
} else {
// 导航成功,隐藏加载指示器
isLoading.value = false
}
} catch (error) {
isLoading.value = false
console.error('导航错误:', error)
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
场景 4:智能重试机制
javascript
async function robustNavigation(path, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const result = await router.push(path)
if (!result) {
return true // 导航成功
}
// 如果是重复导航,不算失败
if (isNavigationFailure(result, NavigationFailureType.duplicated)) {
return true
}
console.log(`导航尝试 ${attempt} 失败:`, result.type)
// 等待后重试
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 1000 * attempt))
}
} catch (error) {
console.error(`导航尝试 ${attempt} 错误:`, error)
}
}
throw new Error(`导航失败,已尝试 ${maxRetries} 次`)
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
最佳实践
1. 总是检查导航结果
javascript
// Good: 检查导航结果
const result = await router.push('/target')
if (result) {
handleNavigationFailure(result)
}
// Avoid: 忽略导航结果
await router.push('/target')
// 后续代码立即执行,不知道导航是否成功1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
2. 提供用户反馈
javascript
async function userFriendlyNavigation(path) {
const result = await router.push(path)
if (isNavigationFailure(result, NavigationFailureType.aborted)) {
showToast('导航被中断,可能有未保存的更改')
} else if (isNavigationFailure(result, NavigationFailureType.duplicated)) {
showToast('您已经在目标页面')
}
// 其他情况不需要特别提示
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
3. 错误边界处理
javascript
async function safeNavigation(path) {
try {
const result = await router.push(path)
if (result) {
// 导航失败但有预期,不抛出错误
return { success: false, failure: result }
}
return { success: true }
} catch (error) {
// 真正的意外错误
console.error('意外的导航错误:', error)
return { success: false, error }
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4. 性能监控
javascript
async function monitoredNavigation(path) {
const startTime = performance.now()
const result = await router.push(path)
const duration = performance.now() - startTime
// 记录导航性能
analytics.track('navigation', {
path,
duration,
success: !result,
failureType: result?.type
})
return result
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
🎯 总结:正确处理导航结果是构建高质量 Vue 应用的关键。通过理解不同的导航失败类型和适当的错误处理策略,你可以创建出更加健壮和用户友好的应用程序。