Vue 路由跳转完全指南:8种跳转方式深度解析

Vue 路由跳转完全指南:8种跳转方式深度解析

Vue Router 提供了丰富灵活的路由跳转方式,从最简单的链接到最复杂的编程式导航。本文将全面解析所有跳转方式,并给出最佳实践建议。

一、快速概览:8种跳转方式对比

方式 类型 特点 适用场景
1. <router-link> 声明式 最简单,语义化 菜单、导航链接
2. router.push() 编程式 灵活,可带参数 按钮点击、条件跳转
3. router.replace() 编程式 替换历史记录 登录后跳转、表单提交
4. router.go() 编程式 历史记录导航 前进后退、面包屑
5. 命名路由 声明式/编程式 解耦路径 大型项目、重构友好
6. 路由别名 声明式 多个路径指向同一路由 兼容旧URL、SEO优化
7. 重定向 配置式 自动跳转 默认路由、权限控制
8. 导航守卫 拦截式 控制跳转流程 权限验证、数据预取

二、声明式导航:<router-link>

2.1 基础用法

vue 复制代码
<template>
  <div class="navigation">
    <!-- 1. 基础路径跳转 -->
    <router-link to="/home">首页</router-link>
    
    <!-- 2. 带查询参数 -->
    <router-link to="/user?tab=profile&page=2">
      用户(第2页)
    </router-link>
    
    <!-- 3. 带哈希 -->
    <router-link to="/about#team">关于我们(团队)</router-link>
    
    <!-- 4. 动态路径 -->
    <router-link :to="`/product/${productId}`">
      产品详情
    </router-link>
    
    <!-- 5. 自定义激活样式 -->
    <router-link 
      to="/dashboard" 
      active-class="active-link"
      exact-active-class="exact-active"
    >
      控制面板
    </router-link>
    
    <!-- 6. 替换历史记录 -->
    <router-link to="/login" replace>
      登录(无返回)
    </router-link>
    
    <!-- 7. 自定义标签 -->
    <router-link to="/help" custom v-slot="{ navigate, isActive }">
      <button 
        @click="navigate" 
        :class="{ active: isActive }"
        class="custom-button"
      >
        帮助中心
      </button>
    </router-link>
  </div>
</template>

<script>
export default {
  data() {
    return {
      productId: 123
    }
  }
}
</script>

<style scoped>
.active-link {
  color: #1890ff;
  font-weight: bold;
}

.exact-active {
  border-bottom: 2px solid #1890ff;
}

.custom-button {
  padding: 8px 16px;
  background: #f5f5f5;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.custom-button.active {
  background: #1890ff;
  color: white;
}
</style>

2.2 高级特性

vue 复制代码
<template>
  <!-- 1. 事件监听 -->
  <router-link 
    to="/cart" 
    @click="handleClick"
    @mouseenter="handleHover"
  >
    购物车
  </router-link>
  
  <!-- 2. 禁止跳转 -->
  <router-link 
    to="/restricted" 
    :event="hasPermission ? 'click' : ''"
    :class="{ disabled: !hasPermission }"
  >
    管理员入口
  </router-link>
  
  <!-- 3. 组合式API使用 -->
  <router-link 
    v-for="nav in navList" 
    :key="nav.path"
    :to="nav.path"
    :class="getNavClass(nav)"
  >
    {{ nav.name }}
    <span v-if="nav.badge" class="badge">{{ nav.badge }}</span>
  </router-link>
</template>

<script setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()
const hasPermission = computed(() => true) // 权限逻辑

const navList = [
  { path: '/', name: '首页', exact: true },
  { path: '/products', name: '产品', badge: 'New' },
  { path: '/about', name: '关于' }
]

const getNavClass = (nav) => {
  const isActive = nav.exact 
    ? route.path === nav.path
    : route.path.startsWith(nav.path)
  
  return {
    'nav-item': true,
    'nav-active': isActive,
    'has-badge': !!nav.badge
  }
}
</script>

三、编程式导航

3.1 router.push() - 最常用的跳转

javascript 复制代码
// 方法1:路径字符串
router.push('/home')
router.push('/user/123')
router.push('/search?q=vue')
router.push('/about#contact')

// 方法2:路由对象(推荐)
router.push({
  path: '/user/123'
})

// 方法3:命名路由(最佳实践)
router.push({
  name: 'UserProfile',
  params: { id: 123 }
})

// 方法4:带查询参数
router.push({
  path: '/search',
  query: {
    q: 'vue router',
    page: 2,
    sort: 'desc'
  }
})

// 方法5:带哈希
router.push({
  path: '/document',
  hash: '#installation'
})

// 方法6:带状态(不显示在URL中)
router.push({
  name: 'Checkout',
  state: {
    cartItems: ['item1', 'item2'],
    discountCode: 'SAVE10'
  }
})

// 方法7:动态路径
const userId = 456
const userType = 'vip'
router.push({
  path: `/user/${userId}`,
  query: { type: userType }
})

// 方法8:条件跳转
function navigateTo(target) {
  if (userStore.isLoggedIn) {
    router.push(target)
  } else {
    router.push({
      path: '/login',
      query: { redirect: target.path || target }
    })
  }
}

3.2 router.replace() - 替换当前历史记录

javascript 复制代码
// 场景1:登录后跳转(不让用户返回登录页)
function handleLogin() {
  login().then(() => {
    router.replace('/dashboard') // 替换登录页记录
  })
}

// 场景2:表单提交后
function submitForm() {
  submit().then(() => {
    // 提交成功后,替换当前页
    router.replace({
      name: 'Success',
      query: { formId: this.formId }
    })
  })
}

// 场景3:重定向中间页
// 访问 /redirect?target=/dashboard
router.beforeEach((to, from, next) => {
  if (to.path === '/redirect') {
    const target = to.query.target
    router.replace(target || '/')
    return
  }
  next()
})

// 场景4:错误页面处理
function loadProduct(id) {
  fetchProduct(id).catch(error => {
    // 错误时替换到错误页
    router.replace({
      name: 'Error',
      params: { message: '产品加载失败' }
    })
  })
}

3.3 router.go() - 历史记录导航

javascript 复制代码
// 前进后退
router.go(1)   // 前进1步
router.go(-1)  // 后退1步
router.go(-3)  // 后退3步
router.go(0)   // 刷新当前页

// 快捷方法
router.back()     // 后退 = router.go(-1)
router.forward()  // 前进 = router.go(1)

// 实际应用
const navigationHistory = []

// 记录导航历史
router.afterEach((to, from) => {
  navigationHistory.push({
    from: from.fullPath,
    to: to.fullPath,
    timestamp: Date.now()
  })
})

// 返回指定步骤
function goBackSteps(steps) {
  if (router.currentRoute.value.meta.preventBack) {
    alert('当前页面禁止返回')
    return
  }
  
  router.go(-steps)
}

// 返回首页
function goHome() {
  const currentDepth = navigationHistory.length
  router.go(-currentDepth + 1) // 保留首页
}

// 面包屑导航
const breadcrumbs = computed(() => {
  const paths = []
  let current = router.currentRoute.value
  
  while (current) {
    paths.unshift(current)
    // 根据meta中的parent字段查找父路由
    current = routes.find(r => r.name === current.meta?.parent)
  }
  
  return paths
})

3.4 编程式导航最佳实践

javascript 复制代码
// 1. 封装导航工具函数
export const nav = {
  // 带权限检查的跳转
  pushWithAuth(to, requiredRole = null) {
    if (!authStore.isLoggedIn) {
      return router.push({
        path: '/login',
        query: { redirect: typeof to === 'string' ? to : to.path }
      })
    }
    
    if (requiredRole && !authStore.hasRole(requiredRole)) {
      return router.push('/unauthorized')
    }
    
    return router.push(to)
  },
  
  // 带确认的跳转
  pushWithConfirm(to, message = '确定离开当前页面?') {
    return new Promise((resolve) => {
      if (confirm(message)) {
        router.push(to).then(resolve)
      }
    })
  },
  
  // 新标签页打开
  openInNewTab(to) {
    const route = router.resolve(to)
    window.open(route.href, '_blank')
  },
  
  // 带Loading的跳转
  pushWithLoading(to) {
    loadingStore.show()
    return router.push(to).finally(() => {
      loadingStore.hide()
    })
  }
}

// 2. 使用示例
// 组件中使用
methods: {
  viewProductDetail(product) {
    nav.pushWithAuth({
      name: 'ProductDetail',
      params: { id: product.id }
    }, 'user')
  },
  
  editProduct(product) {
    nav.pushWithConfirm(
      { name: 'ProductEdit', params: { id: product.id } },
      '有未保存的更改,确定要编辑吗?'
    )
  }
}

四、命名路由跳转

4.1 配置和使用

javascript 复制代码
// router/index.js
const routes = [
  {
    path: '/',
    name: 'Home',  // 命名路由
    component: Home
  },
  {
    path: '/user/:userId',
    name: 'UserProfile',  // 命名路由
    component: UserProfile,
    props: true
  },
  {
    path: '/product/:category/:id',
    name: 'ProductDetail',  // 命名路由
    component: ProductDetail
  },
  {
    path: '/search',
    name: 'Search',
    component: Search,
    props: route => ({ query: route.query.q })
  }
]

// 组件中使用命名路由
// 声明式
<router-link :to="{ name: 'UserProfile', params: { userId: 123 } }">
  用户资料
</router-link>

// 编程式
router.push({
  name: 'ProductDetail',
  params: {
    category: 'electronics',
    id: 456
  }
})

// 带查询参数
router.push({
  name: 'Search',
  query: {
    q: 'vue router',
    sort: 'price'
  }
})

4.2 命名路由的优势

javascript 复制代码
// 优势1:路径解耦,重构方便
// 旧路径:/user/:id
// 新路径:/profile/:id
// 只需修改路由配置,无需修改跳转代码

// 优势2:清晰的参数传递
router.push({
  name: 'OrderCheckout',
  params: {
    orderId: 'ORD-2024-001',
    step: 'payment'  // 参数名清晰
  },
  query: {
    coupon: 'SAVE20',
    source: 'cart'
  }
})

// 优势3:嵌套路由跳转
const routes = [
  {
    path: '/admin',
    name: 'Admin',
    component: AdminLayout,
    children: [
      {
        path: 'users',
        name: 'AdminUsers',  // 全名:AdminUsers
        component: AdminUsers
      },
      {
        path: 'settings',
        name: 'AdminSettings',
        component: AdminSettings
      }
    ]
  }
]

// 跳转到嵌套路由
router.push({ name: 'AdminUsers' })  // 自动找到完整路径

五、路由别名和重定向

5.1 路由别名

javascript 复制代码
// 多个路径指向同一组件
const routes = [
  {
    path: '/home',
    alias: ['/index', '/main', '/'],  // 多个别名
    component: Home,
    meta: { title: '首页' }
  },
  {
    path: '/about-us',
    alias: '/company',  // 单个别名
    component: About
  },
  {
    path: '/products/:id',
    alias: '/items/:id',  // 带参数的别名
    component: ProductDetail
  }
]

// 实际应用场景
const routes = [
  // 场景1:SEO优化 - 多个关键词
  {
    path: '/vue-tutorial',
    alias: ['/vue-教程', '/vue-入门', '/vue-guide'],
    component: Tutorial
  },
  
  // 场景2:兼容旧URL
  {
    path: '/new-url',
    alias: ['/old-url', '/legacy-url', '/deprecated-path'],
    component: NewComponent,
    meta: { 
      canonical: '/new-url',  // 告诉搜索引擎主URL
      redirect301: true 
    }
  },
  
  // 场景3:多语言路径
  {
    path: '/en/about',
    alias: ['/zh/about', '/ja/about', '/ko/about'],
    component: About,
    beforeEnter(to, from, next) {
      // 根据路径设置语言
      const lang = to.path.split('/')[1]
      i18n.locale = lang
      next()
    }
  }
]

5.2 路由重定向

javascript 复制代码
// 1. 简单重定向
const routes = [
  {
    path: '/home',
    redirect: '/dashboard'  // 访问/home跳转到/dashboard
  },
  {
    path: '/',
    redirect: '/home'  // 根路径重定向
  }
]

// 2. 命名路由重定向
const routes = [
  {
    path: '/user',
    redirect: { name: 'UserList' }  // 重定向到命名路由
  }
]

// 3. 函数式重定向(动态)
const routes = [
  {
    path: '/user/:id',
    redirect: to => {
      // 根据参数动态重定向
      const userType = getUserType(to.params.id)
      if (userType === 'admin') {
        return { name: 'AdminProfile', params: { id: to.params.id } }
      } else {
        return { name: 'UserProfile', params: { id: to.params.id } }
      }
    }
  }
]

// 4. 实际应用场景
const routes = [
  // 场景1:版本升级重定向
  {
    path: '/v1/products/:id',
    redirect: to => `/products/${to.params.id}?version=v1`
  },
  
  // 场景2:权限重定向
  {
    path: '/admin',
    redirect: to => {
      if (authStore.isAdmin) {
        return '/admin/dashboard'
      } else {
        return '/unauthorized'
      }
    }
  },
  
  // 场景3:临时重定向(维护页面)
  {
    path: '/under-maintenance',
    component: Maintenance,
    meta: { maintenance: true }
  },
  {
    path: '/',
    redirect: () => {
      if (isMaintenanceMode) {
        return '/under-maintenance'
      }
      return '/home'
    }
  },
  
  // 场景4:404页面捕获
  {
    path: '/:pathMatch(.*)*',  // 捕获所有未匹配路径
    name: 'NotFound',
    component: NotFound,
    beforeEnter(to, from, next) {
      // 记录404访问
      log404(to.fullPath)
      next()
    }
  }
]

六、导航守卫控制跳转

6.1 完整的守卫流程

javascript 复制代码
// 完整的导航解析流程
const router = createRouter({
  routes,
  // 全局配置
})

// 1. 导航被触发
// 2. 在失活的组件里调用 beforeRouteLeave 守卫
// 3. 调用全局的 beforeEach 守卫
// 4. 在重用的组件里调用 beforeRouteUpdate 守卫
// 5. 在路由配置里调用 beforeEnter 守卫
// 6. 解析异步路由组件
// 7. 在被激活的组件里调用 beforeRouteEnter 守卫
// 8. 调用全局的 beforeResolve 守卫
// 9. 导航被确认
// 10. 调用全局的 afterEach 守卫
// 11. 触发 DOM 更新
// 12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数

// 实际应用:权限控制流程
const routes = [
  {
    path: '/admin',
    component: AdminLayout,
    meta: { requiresAuth: true, requiresAdmin: true },
    beforeEnter: (to, from, next) => {
      // 路由独享守卫
      if (!authStore.isAdmin) {
        next('/unauthorized')
      } else {
        next()
      }
    },
    children: [
      {
        path: 'dashboard',
        component: AdminDashboard,
        meta: { requiresSuperAdmin: true }
      }
    ]
  }
]

// 全局前置守卫
router.beforeEach((to, from, next) => {
  // 1. 页面标题
  document.title = to.meta.title || '默认标题'
  
  // 2. 权限验证
  if (to.meta.requiresAuth && !authStore.isLoggedIn) {
    next({
      path: '/login',
      query: { redirect: to.fullPath }
    })
    return
  }
  
  // 3. 管理员权限
  if (to.meta.requiresAdmin && !authStore.isAdmin) {
    next('/unauthorized')
    return
  }
  
  // 4. 维护模式检查
  if (to.meta.maintenance && !isMaintenanceMode) {
    next(from.path || '/')
    return
  }
  
  // 5. 滚动行为重置
  if (to.meta.resetScroll) {
    window.scrollTo(0, 0)
  }
  
  next()
})

// 全局解析守卫(适合获取数据)
router.beforeResolve(async (to, from, next) => {
  // 预取数据
  if (to.meta.requiresData) {
    try {
      await store.dispatch('fetchRequiredData', to.params)
      next()
    } catch (error) {
      next('/error')
    }
  } else {
    next()
  }
})

// 全局后置守卫
router.afterEach((to, from) => {
  // 1. 页面访问统计
  analytics.trackPageView(to.fullPath)
  
  // 2. 关闭加载动画
  hideLoading()
  
  // 3. 保存导航历史
  saveNavigationHistory(to, from)
  
  // 4. 更新面包屑
  updateBreadcrumb(to)
})

// 组件内守卫
export default {
  beforeRouteEnter(to, from, next) {
    // 不能访问 this,因为组件还没创建
    // 但可以通过回调访问
    next(vm => {
      // 通过 vm 访问组件实例
      vm.loadData(to.params.id)
    })
  },
  
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 可以访问组件实例 this
    this.productId = to.params.id
    this.fetchProductData()
    next()
  },
  
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 this
    if (this.hasUnsavedChanges) {
      const answer = confirm('有未保存的更改,确定离开吗?')
      if (!answer) {
        next(false) // 取消导航
        return
      }
    }
    next()
  }
}

6.2 守卫组合实践

javascript 复制代码
// 封装守卫函数
const guard = {
  // 认证守卫
  auth: (to, from, next) => {
    if (!authStore.isLoggedIn) {
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  },
  
  // 权限守卫
  role: (requiredRole) => (to, from, next) => {
    if (!authStore.hasRole(requiredRole)) {
      next('/forbidden')
    } else {
      next()
    }
  },
  
  // 功能开关守卫
  feature: (featureName) => (to, from, next) => {
    if (!featureToggle.isEnabled(featureName)) {
      next('/feature-disabled')
    } else {
      next()
    }
  },
  
  // 数据预取守卫
  prefetch: (dataKey) => async (to, from, next) => {
    try {
      await store.dispatch(`fetch${dataKey}`, to.params)
      next()
    } catch (error) {
      next('/error')
    }
  }
}

// 在路由中使用
const routes = [
  {
    path: '/admin',
    component: AdminLayout,
    beforeEnter: [guard.auth, guard.role('admin')],
    children: [
      {
        path: 'analytics',
        component: Analytics,
        beforeEnter: guard.feature('analytics')
      }
    ]
  },
  {
    path: '/product/:id',
    component: ProductDetail,
    beforeEnter: guard.prefetch('Product')
  }
]

七、高级跳转技巧

7.1 路由传参的多种方式

javascript 复制代码
// 方式1:params(路径参数)
// 路由配置:/user/:id
router.push({ path: '/user/123' })
// 或
router.push({ name: 'User', params: { id: 123 } })

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

// 方式3:props(推荐方式)
const routes = [
  {
    path: '/user/:id',
    name: 'User',
    component: User,
    props: true  // params 转为 props
  },
  {
    path: '/product/:id',
    name: 'Product',
    component: Product,
    props: route => ({
      id: Number(route.params.id),
      preview: route.query.preview === 'true'
    })
  }
]

// 方式4:state(不显示在URL中)
router.push({
  name: 'Checkout',
  state: {
    cartItems: [...],
    discount: 'SAVE10',
    source: 'promotion'
  }
})

// 接收state
const route = useRoute()
const cartItems = route.state?.cartItems || []

// 方式5:meta(路由元信息)
const routes = [
  {
    path: '/premium',
    component: Premium,
    meta: {
      requiresSubscription: true,
      subscriptionLevel: 'gold'
    }
  }
]

// 方式6:动态props传递
function navigateWithProps(target, props) {
  // 临时存储props
  const propKey = `temp_props_${Date.now()}`
  sessionStorage.setItem(propKey, JSON.stringify(props))
  
  router.push({
    path: target,
    query: { _props: propKey }
  })
}

// 在目标组件中读取
const route = useRoute()
const propsData = computed(() => {
  const propKey = route.query._props
  if (propKey) {
    const data = JSON.parse(sessionStorage.getItem(propKey) || '{}')
    sessionStorage.removeItem(propKey)
    return data
  }
  return {}
})

7.2 跳转动画和过渡

vue 复制代码
<template>
  <!-- 路由过渡动画 -->
  <router-view v-slot="{ Component, route }">
    <transition 
      :name="route.meta.transition || 'fade'"
      mode="out-in"
      @before-enter="beforeEnter"
      @after-enter="afterEnter"
    >
      <component :is="Component" :key="route.path" />
    </transition>
  </router-view>
</template>

<script>
export default {
  methods: {
    beforeEnter() {
      // 动画开始前
      document.body.classList.add('page-transition')
    },
    afterEnter() {
      // 动画结束后
      document.body.classList.remove('page-transition')
    }
  }
}
</script>

<style>
/* 淡入淡出 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

/* 滑动效果 */
.slide-left-enter-active,
.slide-left-leave-active {
  transition: transform 0.3s ease;
}

.slide-left-enter-from {
  transform: translateX(100%);
}

.slide-left-leave-to {
  transform: translateX(-100%);
}

/* 缩放效果 */
.zoom-enter-active,
.zoom-leave-active {
  transition: all 0.3s ease;
}

.zoom-enter-from {
  opacity: 0;
  transform: scale(0.9);
}

.zoom-leave-to {
  opacity: 0;
  transform: scale(1.1);
}
</style>

7.3 滚动行为控制

javascript 复制代码
const router = createRouter({
  history: createWebHistory(),
  routes,
  
  // 滚动行为控制
  scrollBehavior(to, from, savedPosition) {
    // 1. 返回按钮保持位置
    if (savedPosition) {
      return savedPosition
    }
    
    // 2. 哈希导航
    if (to.hash) {
      return {
        el: to.hash,
        behavior: 'smooth'  // 平滑滚动
      }
    }
    
    // 3. 特定路由滚动到顶部
    if (to.meta.scrollToTop !== false) {
      return { top: 0, behavior: 'smooth' }
    }
    
    // 4. 保持当前位置
    if (to.meta.keepScroll) {
      return false
    }
    
    // 5. 滚动到指定元素
    if (to.meta.scrollTo) {
      return {
        el: to.meta.scrollTo,
        offset: { x: 0, y: 20 }  // 偏移量
      }
    }
    
    // 默认行为
    return { left: 0, top: 0 }
  }
})

八、实际项目应用

8.1 电商网站路由跳转示例

javascript 复制代码
// router/index.js - 电商路由配置
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
    meta: { title: '首页 - 电商平台' }
  },
  {
    path: '/products',
    name: 'ProductList',
    component: ProductList,
    meta: { 
      title: '商品列表',
      keepAlive: true  // 保持组件状态
    },
    props: route => ({
      category: route.query.category,
      sort: route.query.sort || 'default',
      page: parseInt(route.query.page) || 1
    })
  },
  {
    path: '/product/:id(\\d+)',  // 只匹配数字ID
    name: 'ProductDetail',
    component: ProductDetail,
    meta: { 
      title: '商品详情',
      requiresAuth: false
    },
    beforeEnter: async (to, from, next) => {
      // 验证商品是否存在
      try {
        await productStore.fetchProduct(to.params.id)
        next()
      } catch (error) {
        next('/404')
      }
    }
  },
  {
    path: '/cart',
    name: 'ShoppingCart',
    component: ShoppingCart,
    meta: { 
      title: '购物车',
      requiresAuth: true
    }
  },
  {
    path: '/checkout',
    name: 'Checkout',
    component: Checkout,
    meta: { 
      title: '结算',
      requiresAuth: true,
      requiresCart: true  // 需要购物车有商品
    },
    beforeEnter: (to, from, next) => {
      if (cartStore.isEmpty) {
        next({ name: 'ShoppingCart' })
      } else {
        next()
      }
    }
  },
  {
    path: '/order/:orderId',
    name: 'OrderDetail',
    component: OrderDetail,
    meta: { 
      title: '订单详情',
      requiresAuth: true,
      scrollToTop: true
    }
  },
  // ... 其他路由
]

// 组件中使用
export default {
  methods: {
    // 查看商品
    viewProduct(product) {
      this.$router.push({
        name: 'ProductDetail',
        params: { id: product.id },
        query: { 
          source: 'list',
          ref: this.$route.fullPath 
        }
      })
    },
    
    // 加入购物车
    addToCart(product) {
      cartStore.add(product).then(() => {
        // 显示成功提示后跳转
        this.$message.success('加入购物车成功')
        this.$router.push({
          name: 'ShoppingCart',
          query: { added: product.id }
        })
      })
    },
    
    // 立即购买
    buyNow(product) {
      cartStore.add(product).then(() => {
        this.$router.replace({
          name: 'Checkout',
          query: { quick: 'true' }
        })
      })
    },
    
    // 继续购物
    continueShopping() {
      // 返回之前的商品列表,保持筛选状态
      const returnTo = this.$route.query.ref || '/products'
      this.$router.push(returnTo)
    }
  }
}

8.2 后台管理系统路由示例

javascript 复制代码
// 动态路由加载
let dynamicRoutesLoaded = false

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/login',
      name: 'Login',
      component: () => import('@/views/Login.vue'),
      meta: { guest: true }
    },
    {
      path: '/',
      component: Layout,
      children: [
        {
          path: '',
          name: 'Dashboard',
          component: () => import('@/views/Dashboard.vue'),
          meta: { title: '仪表板', icon: 'dashboard' }
        }
      ]
    }
  ]
})

// 动态加载路由
async function loadDynamicRoutes() {
  if (dynamicRoutesLoaded) return
  
  try {
    const userInfo = await authStore.getUserInfo()
    const menus = await menuStore.fetchUserMenus(userInfo.role)
    
    // 转换菜单为路由
    const routes = transformMenusToRoutes(menus)
    
    // 动态添加路由
    routes.forEach(route => {
      router.addRoute('Layout', route)
    })
    
    dynamicRoutesLoaded = true
    
    // 如果当前路由不存在,重定向到首页
    if (!router.hasRoute(router.currentRoute.value.name)) {
      router.replace('/')
    }
  } catch (error) {
    console.error('加载动态路由失败:', error)
    router.push('/error')
  }
}

// 路由守卫
router.beforeEach(async (to, from, next) => {
  // 显示加载中
  loadingBar.start()
  
  // 登录检查
  if (to.meta.requiresAuth && !authStore.isLoggedIn) {
    next({
      name: 'Login',
      query: { redirect: to.fullPath }
    })
    return
  }
  
  // 游客页面检查(已登录用户不能访问登录页)
  if (to.meta.guest && authStore.isLoggedIn) {
    next('/')
    return
  }
  
  // 加载动态路由
  if (!dynamicRoutesLoaded && authStore.isLoggedIn) {
    await loadDynamicRoutes()
    // 动态路由加载后重新跳转
    next(to.fullPath)
    return
  }
  
  // 权限检查
  if (to.meta.permissions) {
    const hasPermission = checkPermission(to.meta.permissions)
    if (!hasPermission) {
      next('/403')
      return
    }
  }
  
  next()
})

router.afterEach((to) => {
  // 设置页面标题
  document.title = to.meta.title ? `${to.meta.title} - 后台管理` : '后台管理'
  
  // 关闭加载
  loadingBar.finish()
  
  // 记录访问日志
  logAccess(to)
})

九、常见问题与解决方案

9.1 路由跳转常见错误

javascript 复制代码
// 错误1:重复跳转相同路由
// ❌ 会报错:NavigationDuplicated
router.push('/current-path')

// ✅ 解决方案:检查当前路由
function safePush(to) {
  if (router.currentRoute.value.path !== to) {
    router.push(to)
  }
}

// 错误2:params 和 path 同时使用
// ❌ params 会被忽略
router.push({
  path: '/user/123',
  params: { id: 456 }  // 这个被忽略!
})

// ✅ 正确:使用命名路由
router.push({
  name: 'User',
  params: { id: 456 }
})

// 错误3:路由未找到
// ❌ 跳转到不存在的路由
router.push('/non-existent')

// ✅ 解决方案:检查路由是否存在
function safeNavigate(to) {
  const resolved = router.resolve(to)
  if (resolved.matched.length > 0) {
    router.push(to)
  } else {
    router.push('/404')
  }
}

// 错误4:组件未加载
// ❌ 异步组件加载失败
router.push({ name: 'AsyncComponent' })

// ✅ 解决方案:添加错误处理
router.push({ name: 'AsyncComponent' }).catch(error => {
  if (error.name === 'NavigationDuplicated') {
    // 忽略重复导航错误
    return
  }
  
  // 其他错误处理
  console.error('导航失败:', error)
  router.push('/error')
})

9.2 性能优化建议

javascript 复制代码
// 1. 路由懒加载
const routes = [
  {
    path: '/heavy-page',
    component: () => import(/* webpackChunkName: "heavy" */ '@/views/HeavyPage.vue')
  }
]

// 2. 组件预加载
// 在适当的时候预加载路由组件
function prefetchRoute(routeName) {
  const route = router.getRoutes().find(r => r.name === routeName)
  if (route && typeof route.components?.default === 'function') {
    route.components.default()
  }
}

// 在鼠标悬停时预加载
<router-link 
  :to="{ name: 'HeavyPage' }"
  @mouseenter="prefetchRoute('HeavyPage')"
>
  重量级页面
</router-link>

// 3. 路由缓存
// 使用 keep-alive 缓存常用页面
<router-view v-slot="{ Component, route }">
  <keep-alive :include="cachedRoutes">
    <component :is="Component" :key="route.fullPath" />
  </keep-alive>
</router-view>

// 4. 滚动位置缓存
const scrollPositions = new Map()

router.beforeEach((to, from) => {
  // 保存离开时的滚动位置
  if (from.meta.keepScroll) {
    scrollPositions.set(from.fullPath, {
      x: window.scrollX,
      y: window.scrollY
    })
  }
})

router.afterEach((to, from) => {
  // 恢复滚动位置
  if (to.meta.keepScroll && from.meta.keepScroll) {
    const position = scrollPositions.get(to.fullPath)
    if (position) {
      window.scrollTo(position.x, position.y)
    }
  }
})

十、总结

路由跳转选择指南

javascript 复制代码
// 根据场景选择跳转方式
const navigationGuide = {
  // 场景:普通链接
  普通链接: '使用 <router-link>',
  
  // 场景:按钮点击跳转
  按钮点击: '使用 router.push()',
  
  // 场景:表单提交后
  表单提交: '使用 router.replace() 避免重复提交',
  
  // 场景:返回上一步
  返回操作: '使用 router.back() 或 router.go(-1)',
  
  // 场景:权限验证后跳转
  权限跳转: '在导航守卫中控制',
  
  // 场景:动态路由
  动态路由: '使用 router.addRoute() 动态添加',
  
  // 场景:404处理
  未找到页面: '配置 catch-all 路由',
  
  // 场景:平滑过渡
  页面过渡: '使用 <transition> 包裹 <router-view>'
}

// 最佳实践总结
const bestPractices = `
1. 尽量使用命名路由,提高代码可维护性
2. 复杂参数传递使用 props 而不是直接操作 $route
3. 重要跳转添加 loading 状态和错误处理
4. 合理使用导航守卫进行权限控制
5. 移动端考虑滑动返回等交互
6. SEO 重要页面使用静态路径
7. 适当使用路由缓存提升性能
8. 监控路由跳转错误和异常
`

Vue Router 提供了强大而灵活的路由跳转机制,掌握各种跳转方式并根据场景合理选择,可以显著提升应用的用户体验和开发效率。记住:简单的用声明式,复杂的用编程式,全局的控制用守卫

相关推荐
北辰alk2 小时前
Vue v-for 遍历对象顺序完全指南:从混乱到可控
vue.js
m0_471199632 小时前
【场景】如何快速接手一个前端项目
前端·vue.js·react.js
北辰alk2 小时前
Vue Router 中 route 和 router 的终极区别指南
vue.js
Tigger2 小时前
用 Vue 3 做了一套年会抽奖工具,顺便踩了些坑
前端·javascript·vue.js
OpenTiny社区2 小时前
OpenTiny 2025年度贡献者榜单正式公布~
前端·javascript·vue.js
biubiubiu07063 小时前
Vue脚手架创建项目记录
javascript·vue.js·ecmascript
北辰alk3 小时前
Vue 表单修饰符 .lazy:性能优化的秘密武器
vue.js
北辰alk3 小时前
`active-class`:Vue Router 链接组件的激活状态管理
vue.js
北辰alk3 小时前
Vue Router 参数传递:params vs query 深度解析
vue.js