63. 编程式导航方法大全

📖 概述

编程式导航是Vue Router中用于通过JavaScript代码控制路由跳转的方式,与声明式导航(<router-link>)相对应。在实际开发中,编程式导航提供了更灵活的路由控制能力,适用于动态导航、条件导航等场景。本集将深入讲解Vue Router 4.x的编程式导航方法,包括pushreplacegobackforward等核心API,以及导航守卫、路由状态管理等高级应用,帮助你掌握Vue 3路由系统的完整控制能力。

✨ 核心知识点

1. 编程式导航基础

什么是编程式导航

  • 编程式导航是通过JavaScript代码调用Vue Router API来实现路由跳转
  • 提供了比声明式导航更灵活的控制能力
  • 适用于动态路由、条件跳转、表单提交后跳转等场景

编程式导航与声明式导航的对比

特性 编程式导航 声明式导航
语法 router.push() <router-link to="/path">
灵活性 高,支持动态参数、条件跳转 中等,主要用于静态导航
使用场景 动态导航、条件导航、表单提交后跳转 静态导航链接
控制能力 可控制导航时机、处理导航结果 自动处理导航

2. 核心导航方法

1. router.push()

最常用的导航方法,用于跳转到新路由,会向浏览器历史栈添加新记录。

import { useRouter } from 'vue-router'

const router = useRouter()

// 字符串路径
router.push('/home')

// 命名路由 + 参数
router.push({ name: 'user', params: { id: '123' } })

// 带查询参数
router.push({ path: '/search', query: { q: 'vue' } })

// 带hash
router.push({ path: '/about', hash: '#section1' })

// 组合使用
router.push({ 
  name: 'user', 
  params: { id: '123' }, 
  query: { tab: 'profile' } 
})

2. router.replace()

替换当前路由,不会向浏览器历史栈添加新记录,而是替换当前记录。

// 用法与push类似
router.replace('/home')
router.replace({ name: 'user', params: { id: '123' } })

3. router.go()

在浏览器历史栈中前进或后退指定步数。

// 前进1步,相当于history.forward()
router.go(1)

// 后退1步,相当于history.back()
router.go(-1)

// 前进3步
router.go(3)

// 如果步数超出历史栈范围,不会报错,会静默失败
router.go(100)
router.go(-100)

4. router.back()

后退1步,相当于router.go(-1)

router.back()

5. router.forward()

前进1步,相当于router.go(1)

router.forward()

3. 导航方法的返回值

Promise返回值

Vue Router 3.x中,导航方法没有返回值,无法处理导航结果。Vue Router 4.x中,所有导航方法都返回一个Promise,可以处理导航成功或失败。

// 处理导航结果
router.push('/home')
  .then(() => {
    // 导航成功
    console.log('导航成功')
  })
  .catch((error) => {
    // 导航失败
    console.error('导航失败:', error)
  })

// 异步函数中使用
async function navigate() {
  try {
    await router.push('/home')
    console.log('导航成功')
  } catch (error) {
    console.error('导航失败:', error)
  }
}

导航失败的情况

  • 导航守卫返回false
  • 导航守卫抛出错误
  • 导航被取消(如重复导航到同一地址)
  • 路由不存在

4. 高级导航配置

1. 导航状态(state)

可以在导航时传递状态,这些状态会被存储在浏览器历史记录中。

// 导航时传递状态
router.push({
  path: '/home',
  state: { from: 'dashboard' }
})

// 在组件中获取状态
import { useRoute } from 'vue-router'

const route = useRoute()
// 获取导航状态
const from = route.state?.from

2. 滚动行为(scrollBehavior)

配置路由切换时的滚动行为。

// src/router/index.ts
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes,
  // 配置滚动行为
  scrollBehavior(to, from, savedPosition) {
    // 如果有保存的位置,恢复到该位置(如浏览器前进后退)
    if (savedPosition) {
      return savedPosition
    } else {
      // 否则滚动到顶部
      return { top: 0 }
    }
  }
})

3. 滚动行为的高级配置

scrollBehavior(to, from, savedPosition) {
  // 滚动到指定元素
  if (to.hash) {
    return {
      el: to.hash,
      behavior: 'smooth' // 平滑滚动
    }
  }
  
  // 滚动到指定位置
  if (to.query.scrollTo) {
    return {
      top: parseInt(to.query.scrollTo as string)
    }
  }
  
  // 默认滚动到顶部
  return { top: 0 }
}

5. 组合式API中的导航

1. useRouter和useRoute

在Composition API中,使用useRouteruseRoute函数获取路由实例和当前路由信息。

<script setup lang="ts">
import { useRouter, useRoute } from 'vue-router'

const router = useRouter()
const route = useRoute()

function navigateToUser(id: string) {
  router.push({ name: 'user', params: { id } })
}

// 获取当前路由信息
const currentPath = route.path
const currentParams = route.params
const currentQuery = route.query
</script>

2. 监听路由变化

<script setup lang="ts">
import { useRoute, watch } from 'vue-router'

const route = useRoute()

// 监听路由参数变化
watch(
  () => route.params,
  (newParams, oldParams) => {
    // 处理路由参数变化
    console.log('路由参数变化:', newParams, oldParams)
  },
  { deep: true }
)

// 监听查询参数变化
watch(
  () => route.query,
  (newQuery, oldQuery) => {
    // 处理查询参数变化
    console.log('查询参数变化:', newQuery, oldQuery)
  },
  { deep: true }
)
</script>

6. 导航守卫与编程式导航

1. 全局导航守卫

// src/router/index.ts
const router = createRouter({
  // 配置
})

// 全局前置守卫
router.beforeEach((to, from, next) => {
  // 检查是否需要认证
  if (to.meta.requiresAuth && !isAuthenticated) {
    // 重定向到登录页
    next({ name: 'login' })
  } else {
    // 允许导航
    next()
  }
})

// 全局解析守卫
router.beforeResolve((to, from, next) => {
  // 在所有组件内守卫和异步路由组件被解析之后调用
  next()
})

// 全局后置钩子
router.afterEach((to, from) => {
  // 导航完成后调用,可用于统计、埋点等
  console.log('导航完成:', to.path)
})

2. 组件内导航守卫

<script setup lang="ts">
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

// 路由离开前调用
onBeforeRouteLeave((to, from, next) => {
  // 确认是否离开
  if (hasUnsavedChanges.value) {
    if (confirm('您有未保存的更改,确定要离开吗?')) {
      next()
    } else {
      next(false) // 取消导航
    }
  } else {
    next()
  }
})

// 当前路由更新时调用(参数变化)
onBeforeRouteUpdate((to, from, next) => {
  // 处理路由参数变化
  console.log('路由参数更新:', to.params)
  next()
})
</script>

3. 导航守卫与编程式导航的交互

// 编程式导航会触发导航守卫
router.push('/protected')
  .then(() => {
    console.log('导航成功')
  })
  .catch((error) => {
    // 如果导航守卫返回false或抛出错误,会进入catch
    console.error('导航被阻止:', error)
  })

7. 动态路由与编程式导航

1. 动态添加路由

// 动态添加单个路由
router.addRoute({
  path: '/dynamic',
  name: 'dynamic',
  component: () => import('../views/DynamicView.vue')
})

// 添加嵌套路由
router.addRoute('parent', {
  path: 'child',
  name: 'child',
  component: () => import('../views/ChildView.vue')
})

// 添加多个路由
router.addRoute([
  {
    path: '/route1',
    name: 'route1',
    component: () => import('../views/Route1.vue')
  },
  {
    path: '/route2',
    name: 'route2',
    component: () => import('../views/Route2.vue')
  }
])

2. 动态移除路由

// 通过名称移除路由
router.removeRoute('dynamic')

// 通过返回值移除路由
const removeRoute = router.addRoute({
  path: '/temp',
  name: 'temp',
  component: () => import('../views/TempView.vue')
})

// 移除刚添加的路由
removeRoute()

3. 获取路由列表

// 获取所有路由
const routes = router.getRoutes()

// 查找特定路由
const userRoute = router.resolve({ name: 'user', params: { id: '123' } })
console.log(userRoute.href) // 输出:/user/123

8. 导航错误处理

1. 捕获导航错误

// 捕获导航错误
router.push('/non-existent-route')
  .catch((error) => {
    console.error('导航错误:', error)
    // 处理错误,如显示404页面
    router.push('/404')
  })

2. 常见导航错误类型

  • NavigationDuplicated:重复导航到同一路由
  • NotFoundError:路由不存在
  • NavigationCancelled:导航被取消
  • NavigationAborted:导航被中止

3. 处理重复导航

// 忽略重复导航错误
router.push('/home')
  .catch((error) => {
    if (error.name !== 'NavigationDuplicated') {
      throw error
    }
  })

9. 高级导航技巧

1. 导航预加载

// 预加载路由组件
const preloadRoute = (name: string) => {
  const route = router.getRoutes().find(r => r.name === name)
  if (route && typeof route.component === 'function') {
    // 调用组件加载函数,但不渲染
    route.component()
  }
}

// 在用户可能导航之前预加载
preloadRoute('user')

2. 条件导航

function conditionalNavigate() {
  if (user.hasPermission('admin')) {
    router.push('/admin')
  } else if (user.hasPermission('user')) {
    router.push('/user')
  } else {
    router.push('/guest')
  }
}

3. 延迟导航

function delayedNavigate() {
  // 延迟1秒后导航
  setTimeout(() => {
    router.push('/delayed')
  }, 1000)
}

// 或者使用Promise
async function delayedNavigate() {
  await new Promise(resolve => setTimeout(resolve, 1000))
  await router.push('/delayed')
}

📝 最佳实践

  1. 优先使用命名路由

    • 提高代码可读性和可维护性
    • 便于路由重构
    • 避免硬编码路径
  2. 处理导航结果

    • 始终处理router.push()等方法的返回Promise
    • 捕获导航错误,提供友好的用户反馈
  3. 合理使用导航守卫

    • 全局守卫用于权限控制、路由切换动画等
    • 组件内守卫用于处理组件特定的导航逻辑
    • 避免在守卫中执行复杂的异步操作
  4. 优化滚动行为

    • 配置合理的滚动行为,提高用户体验
    • 使用平滑滚动(behavior: &#39;smooth&#39;)增强交互体验
  5. 动态路由管理

    • 根据权限动态添加或移除路由
    • 合理组织路由结构,便于动态管理
  6. 避免重复导航

    • 处理NavigationDuplicated错误
    • 提供视觉反馈,避免用户重复点击
  7. 使用组合式API

    • 在Vue 3中优先使用useRouteruseRoute
    • 与Composition API无缝配合,提高代码可组合性
  8. 导航状态管理

    • 使用state参数传递导航上下文
    • 避免使用全局状态管理导航信息

💡 常见问题与解决方案

  1. 导航守卫中next()调用时机

    • 确保在所有异步操作完成后调用next()
    • 避免多次调用next()
  2. 重复导航错误

    • 忽略NavigationDuplicated错误
    • 使用router.replace()替代router.push()
  3. 路由参数不更新

    • 使用onBeforeRouteUpdate监听参数变化
    • 避免在组件初始化时只获取一次参数
  4. 滚动行为不生效

    • 检查scrollBehavior配置是否正确
    • 确保路由切换是通过Vue Router进行的
  5. 动态路由不显示

    • 检查动态路由是否正确添加
    • 确保路由组件加载成功
  6. 导航守卫与异步操作

    • 在异步操作完成后调用next()
    • 使用async/await处理异步逻辑

📚 进一步学习资源

🎯 课后练习

  1. 基础练习

    • 使用router.push()实现不同类型的导航
    • 测试router.replace()router.go()等方法
    • 实现简单的导航守卫
  2. 进阶练习

    • 配置自定义滚动行为
    • 实现导航错误处理
    • 使用组合式API处理路由
  3. 实战练习

    • 实现带权限控制的导航系统
    • 构建动态路由管理系统
    • 实现导航预加载功能
  4. 性能优化练习

    • 优化路由切换性能
    • 实现高效的动态路由管理
    • 测试不同导航方式的性能差异

通过本集的学习,你已经掌握了Vue Router 4.x的编程式导航方法,包括核心导航API、导航守卫、动态路由管理、导航错误处理等高级应用。在实际项目中,合理运用这些导航方法,能够构建出功能完整、用户体验良好的单页应用。下一集我们将深入学习路由参数与查询参数,进一步提升Vue 3路由系统的使用能力。

« 上一篇 Vue3 + TypeScript 系列教程 - 第62集:路由配置与嵌套路由 下一篇 » Vue3 + TypeScript 系列教程 - 第64集:路由参数与查询参数