Skip to content

编程式导航 | Vue Router 代码控制路由完全指南 🧭

不只是点击链接!用代码精确控制用户的导航体验,让路由跳转更智能、更灵活。

🌟 什么是编程式导航?

编程式导航是指通过 JavaScript 代码来控制路由跳转,而不是依赖用户点击 <router-link> 组件。这让你能够:

  • 🎯 条件导航 - 根据业务逻辑决定是否跳转
  • 🔄 动态跳转 - 基于用户操作或数据变化触发导航
  • 🛡️ 权限控制 - 在跳转前进行权限验证
  • 🎮 交互体验 - 结合动画、确认框等增强用户体验

💡 声明式 vs 编程式导航

声明式导航编程式导航
<router-link :to="...">router.push(...)
用户主动点击代码逻辑控制
静态跳转动态跳转

🚀 核心 API 详解

🎯 router.push() - 导航到新位置

router.push() 是最常用的编程式导航方法,它会向浏览器历史栈添加新记录。

📝 基础语法

javascript
// 🎯 获取路由器实例
import { useRouter } from 'vue-router'

const router = useRouter()

// 🚀 各种跳转方式
router.push('/users/john')                    // 字符串路径
router.push({ path: '/users/john' })          // 路径对象
router.push({ name: 'user', params: { id: 'john' } })  // 命名路由
router.push({ path: '/search', query: { q: 'vue' } })  // 带查询参数
router.push({ path: '/about', hash: '#team' })         // 带锚点

🎮 实战示例 - 智能登录跳转

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

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

// 🔐 登录状态管理
const isLoggedIn = ref(false)
const userRole = ref('guest')
const loginForm = ref({
  username: '',
  password: ''
})

// 🎯 智能跳转逻辑
const getRedirectPath = () => {
  // 1. 检查是否有重定向参数
  const redirect = route.query.redirect
  if (redirect) return redirect
  
  // 2. 根据用户角色决定默认页面
  const roleRoutes = {
    admin: { name: 'admin-dashboard' },
    user: { name: 'user-profile' },
    guest: { name: 'home' }
  }
  
  return roleRoutes[userRole.value] || { name: 'home' }
}

// 🚀 执行登录
async function handleLogin() {
  try {
    // 🔄 显示加载状态
    const loading = showLoading('正在登录...')
    
    // 🌐 调用登录 API
    const response = await loginAPI(loginForm.value)
    
    if (response.success) {
      // ✅ 登录成功
      isLoggedIn.value = true
      userRole.value = response.user.role
      
      // 🎉 显示成功消息
      showSuccess('登录成功!正在跳转...')
      
      // 🎯 智能跳转
      const redirectTo = getRedirectPath()
      
      // ⏰ 延迟跳转,让用户看到成功消息
      setTimeout(() => {
        router.push(redirectTo)
      }, 1000)
      
    } else {
      // ❌ 登录失败
      showError(response.message || '登录失败,请检查用户名和密码')
    }
  } catch (error) {
    // 🚨 网络错误
    showError('网络连接失败,请稍后重试')
  } finally {
    hideLoading()
  }
}

// 🎯 条件导航示例
function navigateToProfile() {
  if (!isLoggedIn.value) {
    // 🔐 未登录,跳转到登录页并记录当前页面
    router.push({
      name: 'login',
      query: { redirect: route.fullPath }
    })
    return
  }
  
  // ✅ 已登录,直接跳转到个人资料
  router.push({ name: 'user-profile' })
}

// 🛒 购物车结算流程
function proceedToCheckout() {
  // 1. 检查登录状态
  if (!isLoggedIn.value) {
    showConfirm({
      title: '需要登录',
      message: '请先登录后再进行结算',
      confirmText: '去登录',
      cancelText: '取消',
      onConfirm: () => {
        router.push({
          name: 'login',
          query: { redirect: '/checkout' }
        })
      }
    })
    return
  }
  
  // 2. 检查购物车
  if (cartItems.value.length === 0) {
    showAlert('购物车为空,请先添加商品')
    router.push({ name: 'product-list' })
    return
  }
  
  // 3. 检查收货地址
  if (!hasShippingAddress.value) {
    showConfirm({
      title: '需要收货地址',
      message: '请先设置收货地址',
      confirmText: '去设置',
      cancelText: '取消',
      onConfirm: () => {
        router.push({ name: 'address-management' })
      }
    })
    return
  }
  
  // ✅ 所有条件满足,进入结算页面
  router.push({ name: 'checkout' })
}
</script>

<template>
  <div class="login-container">
    <!-- 🔐 登录表单 -->
    <form @submit.prevent="handleLogin" class="login-form">
      <h2>🔐 用户登录</h2>
      
      <div class="form-group">
        <label for="username">👤 用户名</label>
        <input
          id="username"
          v-model="loginForm.username"
          type="text"
          placeholder="请输入用户名"
          required
        />
      </div>
      
      <div class="form-group">
        <label for="password">🔒 密码</label>
        <input
          id="password"
          v-model="loginForm.password"
          type="password"
          placeholder="请输入密码"
          required
        />
      </div>
      
      <button type="submit" class="login-btn">
        🚀 立即登录
      </button>
    </form>
    
    <!-- 🎯 导航示例 -->
    <div class="navigation-examples">
      <h3>🧭 导航示例</h3>
      
      <button @click="navigateToProfile()" class="nav-btn">
        👤 查看个人资料
      </button>
      
      <button @click="proceedToCheckout()" class="nav-btn">
        🛒 立即结算
      </button>
      
      <button @click="router.push({ name: 'product-list', query: { category: 'electronics' } })" class="nav-btn">
        📱 浏览电子产品
      </button>
    </div>
  </div>
</template>

<style scoped>
.login-container {
  max-width: 400px;
  margin: 40px auto;
  padding: 32px;
  background: white;
  border-radius: 16px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
}

.login-form {
  margin-bottom: 32px;
}

.login-form h2 {
  text-align: center;
  margin-bottom: 24px;
  color: #1f2937;
}

.form-group {
  margin-bottom: 20px;
}

.form-group label {
  display: block;
  margin-bottom: 8px;
  font-weight: 500;
  color: #374151;
}

.form-group input {
  width: 100%;
  padding: 12px 16px;
  border: 2px solid #e5e7eb;
  border-radius: 8px;
  font-size: 16px;
  transition: border-color 0.3s ease;
}

.form-group input:focus {
  outline: none;
  border-color: #3b82f6;
}

.login-btn {
  width: 100%;
  padding: 14px;
  background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
  color: white;
  border: none;
  border-radius: 8px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s ease;
}

.login-btn:hover {
  background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%);
  transform: translateY(-2px);
}

.navigation-examples h3 {
  margin-bottom: 16px;
  color: #1f2937;
}

.nav-btn {
  display: block;
  width: 100%;
  margin-bottom: 12px;
  padding: 12px 16px;
  background: #f8fafc;
  border: 2px solid #e2e8f0;
  border-radius: 8px;
  color: #374151;
  cursor: pointer;
  transition: all 0.3s ease;
}

.nav-btn:hover {
  background: #e2e8f0;
  border-color: #cbd5e1;
}
</style>

🔄 router.replace() - 替换当前位置

router.replace()router.push() 类似,但不会在历史栈中添加新记录,而是替换当前记录。

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

const router = useRouter()

// 🔄 替换当前路由
function replaceRoute() {
  router.replace({ name: 'new-page' })
}

// 🎯 等价写法
function replaceWithPush() {
  router.push({ name: 'new-page', replace: true })
}

// 🚀 实际应用场景
function handleFormSubmit() {
  // 表单提交成功后,替换当前页面为成功页面
  // 这样用户点击后退按钮时不会回到表单页面
  router.replace({ 
    name: 'form-success',
    query: { message: '提交成功' }
  })
}

// 🔐 登录重定向
function redirectAfterLogin() {
  // 登录成功后替换登录页面,防止用户后退到登录页
  const redirectTo = route.query.redirect || '/dashboard'
  router.replace(redirectTo)
}
</script>

<template>
  <div class="replace-examples">
    <h3>🔄 Replace 导航示例</h3>
    
    <button @click="replaceRoute()" class="btn btn-primary">
      🔄 替换当前页面
    </button>
    
    <button @click="handleFormSubmit()" class="btn btn-success">
      📝 提交表单(替换)
    </button>
    
    <button @click="redirectAfterLogin()" class="btn btn-info">
      🔐 登录重定向
    </button>
  </div>
</template>

⏭️ 历史导航 - router.go()、router.back()、router.forward()

控制浏览器历史记录的前进和后退。

vue
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'

const router = useRouter()
const historyStep = ref(1)

// ⬅️ 后退一步
function goBack() {
  router.back()
  // 等价于 router.go(-1)
}

// ➡️ 前进一步
function goForward() {
  router.forward()
  // 等价于 router.go(1)
}

// 🎯 自定义步数
function goSteps(steps) {
  router.go(steps)
}

// 🔄 智能导航历史
function smartHistoryNavigation() {
  // 检查是否有历史记录
  if (window.history.length > 1) {
    router.back()
  } else {
    // 没有历史记录,跳转到首页
    router.push({ name: 'home' })
  }
}

// 📱 移动端友好的返回
function mobileBack() {
  // 在移动端,优先使用物理返回键的行为
  if (window.history.length > 1) {
    router.back()
  } else {
    // 如果是直接访问的页面,返回到合适的父级页面
    const currentRoute = router.currentRoute.value
    
    if (currentRoute.name?.includes('product-detail')) {
      router.push({ name: 'product-list' })
    } else if (currentRoute.name?.includes('user-')) {
      router.push({ name: 'user-profile' })
    } else {
      router.push({ name: 'home' })
    }
  }
}
</script>

<template>
  <div class="history-navigation">
    <h3>📚 历史导航控制</h3>
    
    <!-- 🎮 基础控制 -->
    <div class="basic-controls">
      <button @click="goBack()" class="btn btn-outline">
        ⬅️ 后退
      </button>
      
      <button @click="goForward()" class="btn btn-outline">
        ➡️ 前进
      </button>
    </div>
    
    <!-- 🎯 自定义步数 -->
    <div class="custom-steps">
      <label for="steps">跳转步数:</label>
      <input
        id="steps"
        v-model.number="historyStep"
        type="number"
        min="-10"
        max="10"
        class="step-input"
      />
      <button @click="goSteps(historyStep)" class="btn btn-primary">
        🎯 跳转 {{ historyStep }} 步
      </button>
    </div>
    
    <!-- 📱 智能返回 -->
    <div class="smart-controls">
      <button @click="smartHistoryNavigation()" class="btn btn-success">
        🧠 智能返回
      </button>
      
      <button @click="mobileBack()" class="btn btn-info">
        📱 移动端返回
      </button>
    </div>
  </div>
</template>

<style scoped>
.history-navigation {
  padding: 24px;
  background: #f8fafc;
  border-radius: 12px;
  margin: 20px 0;
}

.basic-controls,
.custom-steps,
.smart-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.step-input {
  width: 80px;
  padding: 8px 12px;
  border: 2px solid #e5e7eb;
  border-radius: 6px;
  text-align: center;
}

.btn {
  padding: 10px 16px;
  border: none;
  border-radius: 6px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.3s ease;
}

.btn-outline {
  background: transparent;
  color: #374151;
  border: 2px solid #d1d5db;
}

.btn-outline:hover {
  background: #f3f4f6;
  border-color: #9ca3af;
}

.btn-primary {
  background: #3b82f6;
  color: white;
}

.btn-primary:hover {
  background: #2563eb;
}

.btn-success {
  background: #10b981;
  color: white;
}

.btn-success:hover {
  background: #059669;
}

.btn-info {
  background: #0ea5e9;
  color: white;
}

.btn-info:hover {
  background: #0284c7;
}
</style>

🎯 高级用法

🔄 异步导航与 Promise

所有导航方法都返回 Promise,让你能够处理导航的成功或失败。

vue
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'

const router = useRouter()
const isNavigating = ref(false)
const navigationError = ref(null)

// 🚀 异步导航处理
async function navigateWithFeedback(to) {
  try {
    isNavigating.value = true
    navigationError.value = null
    
    // 🎯 执行导航
    await router.push(to)
    
    // ✅ 导航成功
    showSuccess('页面跳转成功!')
    
  } catch (error) {
    // ❌ 导航失败(可能被导航守卫阻止)
    navigationError.value = error.message
    showError(`导航失败: ${error.message}`)
    
  } finally {
    isNavigating.value = false
  }
}

// 🎮 批量导航
async function navigateSequence(routes) {
  for (const route of routes) {
    try {
      await router.push(route)
      // ⏰ 每次导航间隔
      await new Promise(resolve => setTimeout(resolve, 1000))
    } catch (error) {
      console.error(`导航到 ${route} 失败:`, error)
      break
    }
  }
}

// 🔄 重试导航
async function retryNavigation(to, maxRetries = 3) {
  let retries = 0
  
  while (retries < maxRetries) {
    try {
      await router.push(to)
      return // 成功,退出重试循环
    } catch (error) {
      retries++
      console.warn(`导航重试 ${retries}/${maxRetries}:`, error)
      
      if (retries >= maxRetries) {
        throw new Error(`导航失败,已重试 ${maxRetries} 次`)
      }
      
      // ⏰ 重试前等待
      await new Promise(resolve => setTimeout(resolve, 1000 * retries))
    }
  }
}

// 🎯 条件导航链
async function conditionalNavigationChain() {
  try {
    // 1. 检查用户权限
    const hasPermission = await checkUserPermission()
    if (!hasPermission) {
      await router.push({ name: 'access-denied' })
      return
    }
    
    // 2. 预加载数据
    const data = await preloadData()
    if (!data) {
      await router.push({ name: 'data-error' })
      return
    }
    
    // 3. 最终导航
    await router.push({
      name: 'target-page',
      params: { id: data.id },
      state: { preloadedData: data }
    })
    
  } catch (error) {
    // 🚨 任何步骤失败都跳转到错误页面
    await router.push({
      name: 'error',
      query: { message: error.message }
    })
  }
}
</script>

<template>
  <div class="async-navigation">
    <h3>🔄 异步导航示例</h3>
    
    <!-- 🎯 基础异步导航 -->
    <div class="nav-section">
      <button 
        @click="navigateWithFeedback({ name: 'user-profile' })"
        :disabled="isNavigating"
        class="btn btn-primary"
      >
        <span v-if="isNavigating">🔄 跳转中...</span>
        <span v-else>👤 查看个人资料</span>
      </button>
      
      <div v-if="navigationError" class="error-message">
        ❌ {{ navigationError }}
      </div>
    </div>
    
    <!-- 🎮 批量导航 -->
    <div class="nav-section">
      <button 
        @click="navigateSequence([
          { name: 'page1' },
          { name: 'page2' },
          { name: 'page3' }
        ])"
        class="btn btn-secondary"
      >
        🎮 批量导航演示
      </button>
    </div>
    
    <!-- 🔄 重试导航 -->
    <div class="nav-section">
      <button 
        @click="retryNavigation({ name: 'unstable-page' })"
        class="btn btn-warning"
      >
        🔄 重试导航
      </button>
    </div>
    
    <!-- 🎯 条件导航链 -->
    <div class="nav-section">
      <button 
        @click="conditionalNavigationChain()"
        class="btn btn-success"
      >
        🎯 智能导航链
      </button>
    </div>
  </div>
</template>

<style scoped>
.async-navigation {
  padding: 24px;
  background: white;
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}

.nav-section {
  margin-bottom: 20px;
  padding: 16px;
  background: #f8fafc;
  border-radius: 8px;
}

.error-message {
  margin-top: 12px;
  padding: 12px;
  background: #fef2f2;
  color: #dc2626;
  border-radius: 6px;
  border-left: 4px solid #ef4444;
}

.btn {
  padding: 12px 20px;
  border: none;
  border-radius: 8px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.3s ease;
}

.btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.btn-primary {
  background: #3b82f6;
  color: white;
}

.btn-secondary {
  background: #6b7280;
  color: white;
}

.btn-warning {
  background: #f59e0b;
  color: white;
}

.btn-success {
  background: #10b981;
  color: white;
}
</style>

🎨 导航状态管理

vue
<script setup>
import { ref, computed, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'

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

// 🎯 导航状态
const navigationState = ref({
  isNavigating: false,
  previousRoute: null,
  navigationHistory: [],
  pendingNavigation: null
})

// 📊 导航统计
const navigationStats = computed(() => ({
  totalNavigations: navigationState.value.navigationHistory.length,
  uniquePages: new Set(navigationState.value.navigationHistory.map(nav => nav.name)).size,
  averageTimeOnPage: calculateAverageTimeOnPage(),
  mostVisitedPage: getMostVisitedPage()
}))

// 🔍 监听路由变化
watch(route, (newRoute, oldRoute) => {
  if (oldRoute) {
    // 📝 记录导航历史
    navigationState.value.navigationHistory.push({
      from: oldRoute.name,
      to: newRoute.name,
      timestamp: Date.now(),
      params: newRoute.params,
      query: newRoute.query
    })
    
    navigationState.value.previousRoute = oldRoute
  }
}, { immediate: true })

// 🎯 智能导航函数
function smartNavigate(to, options = {}) {
  const {
    confirm = false,
    preload = false,
    analytics = true,
    fallback = null
  } = options
  
  return new Promise(async (resolve, reject) => {
    try {
      // 🔍 确认导航
      if (confirm) {
        const confirmed = await showConfirmDialog({
          title: '确认导航',
          message: `确定要跳转到 ${to.name || to} 吗?`,
          confirmText: '确定',
          cancelText: '取消'
        })
        
        if (!confirmed) {
          reject(new Error('用户取消导航'))
          return
        }
      }
      
      // 📊 预加载数据
      if (preload && to.name) {
        await preloadRouteData(to.name)
      }
      
      // 🎯 执行导航
      navigationState.value.isNavigating = true
      navigationState.value.pendingNavigation = to
      
      await router.push(to)
      
      // 📈 发送分析数据
      if (analytics) {
        sendNavigationAnalytics(to)
      }
      
      resolve()
      
    } catch (error) {
      // 🚨 导航失败,尝试回退方案
      if (fallback) {
        try {
          await router.push(fallback)
          resolve()
        } catch (fallbackError) {
          reject(fallbackError)
        }
      } else {
        reject(error)
      }
    } finally {
      navigationState.value.isNavigating = false
      navigationState.value.pendingNavigation = null
    }
  })
}

// 📊 计算页面平均停留时间
function calculateAverageTimeOnPage() {
  const history = navigationState.value.navigationHistory
  if (history.length < 2) return 0
  
  const times = []
  for (let i = 1; i < history.length; i++) {
    times.push(history[i].timestamp - history[i - 1].timestamp)
  }
  
  return times.reduce((sum, time) => sum + time, 0) / times.length
}

// 🏆 获取最常访问的页面
function getMostVisitedPage() {
  const history = navigationState.value.navigationHistory
  const pageCount = {}
  
  history.forEach(nav => {
    pageCount[nav.to] = (pageCount[nav.to] || 0) + 1
  })
  
  return Object.entries(pageCount)
    .sort(([,a], [,b]) => b - a)[0]?.[0] || null
}

// 🔄 导航回退
function navigateBack(steps = 1) {
  const history = navigationState.value.navigationHistory
  if (history.length >= steps) {
    const targetNav = history[history.length - steps]
    router.push({
      name: targetNav.from,
      params: targetNav.params,
      query: targetNav.query
    })
  } else {
    router.push({ name: 'home' })
  }
}
</script>

<template>
  <div class="navigation-manager">
    <div class="navigation-status">
      <h3>🧭 导航状态管理</h3>
      
      <!-- 🎯 当前状态 -->
      <div class="status-card">
        <div class="status-item">
          <span class="label">当前页面:</span>
          <span class="value">{{ route.name || '未知' }}</span>
        </div>
        
        <div class="status-item">
          <span class="label">上一页面:</span>
          <span class="value">{{ navigationState.previousRoute?.name || '无' }}</span>
        </div>
        
        <div class="status-item">
          <span class="label">导航状态:</span>
          <span class="value" :class="{ 'navigating': navigationState.isNavigating }">
            {{ navigationState.isNavigating ? '🔄 导航中' : '✅ 就绪' }}
          </span>
        </div>
      </div>
      
      <!-- 📊 导航统计 -->
      <div class="stats-card">
        <h4>📊 导航统计</h4>
        <div class="stats-grid">
          <div class="stat-item">
            <div class="stat-value">{{ navigationStats.totalNavigations }}</div>
            <div class="stat-label">总导航次数</div>
          </div>
          
          <div class="stat-item">
            <div class="stat-value">{{ navigationStats.uniquePages }}</div>
            <div class="stat-label">访问页面数</div>
          </div>
          
          <div class="stat-item">
            <div class="stat-value">{{ Math.round(navigationStats.averageTimeOnPage / 1000) }}s</div>
            <div class="stat-label">平均停留时间</div>
          </div>
          
          <div class="stat-item">
            <div class="stat-value">{{ navigationStats.mostVisitedPage || '无' }}</div>
            <div class="stat-label">最常访问</div>
          </div>
        </div>
      </div>
      
      <!-- 🎮 智能导航控制 -->
      <div class="controls-card">
        <h4>🎮 智能导航</h4>
        <div class="control-buttons">
          <button 
            @click="smartNavigate({ name: 'user-profile' }, { confirm: true, preload: true })"
            class="btn btn-primary"
          >
            👤 智能跳转个人资料
          </button>
          
          <button 
            @click="smartNavigate({ name: 'admin-panel' }, { 
              confirm: true, 
              fallback: { name: 'access-denied' } 
            })"
            class="btn btn-warning"
          >
            ⚙️ 尝试访问管理面板
          </button>
          
          <button 
            @click="navigateBack()"
            class="btn btn-secondary"
          >
            ⬅️ 智能返回
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.navigation-manager {
  padding: 24px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border-radius: 16px;
  margin: 20px 0;
}

.status-card,
.stats-card,
.controls-card {
  background: rgba(255, 255, 255, 0.1);
  border-radius: 12px;
  padding: 20px;
  margin-bottom: 20px;
  backdrop-filter: blur(10px);
}

.status-item {
  display: flex;
  justify-content: space-between;
  margin-bottom: 12px;
  padding: 8px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.label {
  font-weight: 500;
  opacity: 0.8;
}

.value {
  font-weight: 600;
}

.value.navigating {
  color: #fbbf24;
  animation: pulse 1s infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 16px;
}

.stat-item {
  text-align: center;
  padding: 16px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 8px;
}

.stat-value {
  font-size: 24px;
  font-weight: bold;
  margin-bottom: 4px;
}

.stat-label {
  font-size: 12px;
  opacity: 0.8;
}

.control-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.btn {
  padding: 12px 20px;
  border: none;
  border-radius: 8px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.3s ease;
  background: rgba(255, 255, 255, 0.2);
  color: white;
}

.btn:hover {
  background: rgba(255, 255, 255, 0.3);
  transform: translateY(-2px);
}

h3, h4 {
  margin-bottom: 16px;
  color: #fbbf24;
}
</style>

🎯 实战案例

🛍️ 电商购物流程

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

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

// 🛒 购物流程状态
const shoppingFlow = ref({
  currentStep: 'browse',
  cart: [],
  user: null,
  shippingAddress: null,
  paymentMethod: null
})

// 🎯 购物流程步骤
const flowSteps = {
  browse: { name: 'product-list', title: '浏览商品', icon: '🛍️' },
  detail: { name: 'product-detail', title: '商品详情', icon: '📱' },
  cart: { name: 'shopping-cart', title: '购物车', icon: '🛒' },
  login: { name: 'login', title: '登录', icon: '🔐' },
  address: { name: 'shipping-address', title: '收货地址', icon: '📍' },
  payment: { name: 'payment-method', title: '支付方式', icon: '💳' },
  checkout: { name: 'checkout', title: '确认订单', icon: '📋' },
  success: { name: 'order-success', title: '订单成功', icon: '🎉' }
}

// 🚀 智能购物导航
async function navigateShoppingFlow(targetStep, productId = null) {
  try {
    const currentStep = shoppingFlow.value.currentStep
    
    // 🎯 根据目标步骤执行不同逻辑
    switch (targetStep) {
      case 'detail':
        if (!productId) {
          throw new Error('商品ID不能为空')
        }
        await router.push({
          name: 'product-detail',
          params: { id: productId }
        })
        break
        
      case 'cart':
        await router.push({ name: 'shopping-cart' })
        break
        
      case 'checkout':
        // 🔍 检查购物流程前置条件
        const checkResult = await checkCheckoutPrerequisites()
        if (!checkResult.success) {
          await handleCheckoutError(checkResult.error)
          return
        }
        
        await router.push({ name: 'checkout' })
        break
        
      case 'login':
        await router.push({
          name: 'login',
          query: { 
            redirect: route.fullPath,
            flow: 'shopping'
          }
        })
        break
        
      default:
        await router.push({ name: flowSteps[targetStep].name })
    }
    
    shoppingFlow.value.currentStep = targetStep
    
  } catch (error) {
    showError(`导航失败: ${error.message}`)
  }
}

// 🔍 检查结算前置条件
async function checkCheckoutPrerequisites() {
  // 1. 检查购物车
  if (shoppingFlow.value.cart.length === 0) {
    return {
      success: false,
      error: 'empty_cart',
      message: '购物车为空'
    }
  }
  
  // 2. 检查登录状态
  if (!shoppingFlow.value.user) {
    return {
      success: false,
      error: 'not_logged_in',
      message: '请先登录'
    }
  }
  
  // 3. 检查收货地址
  if (!shoppingFlow.value.shippingAddress) {
    return {
      success: false,
      error: 'no_address',
      message: '请设置收货地址'
    }
  }
  
  // 4. 检查支付方式
  if (!shoppingFlow.value.paymentMethod) {
    return {
      success: false,
      error: 'no_payment',
      message: '请选择支付方式'
    }
  }
  
  return { success: true }
}

// 🚨 处理结算错误
async function handleCheckoutError(errorType) {
  const errorHandlers = {
    empty_cart: () => navigateShoppingFlow('browse'),
    not_logged_in: () => navigateShoppingFlow('login'),
    no_address: () => navigateShoppingFlow('address'),
    no_payment: () => navigateShoppingFlow('payment')
  }
  
  const handler = errorHandlers[errorType]
  if (handler) {
    await handler()
  }
}

// 🎯 添加商品到购物车并导航
async function addToCartAndNavigate(product) {
  try {
    // 🛒 添加到购物车
    shoppingFlow.value.cart.push(product)
    
    // 🎉 显示成功消息
    showSuccess(`${product.name} 已添加到购物车`)
    
    // 🎯 询问用户下一步操作
    const action = await showActionDialog({
      title: '商品已添加',
      message: '您想要继续购物还是查看购物车?',
      actions: [
        { text: '继续购物', value: 'continue', icon: '🛍️' },
        { text: '查看购物车', value: 'cart', icon: '🛒' },
        { text: '立即结算', value: 'checkout', icon: '💳' }
      ]
    })
    
    switch (action) {
      case 'continue':
        // 继续在当前页面
        break
      case 'cart':
        await navigateShoppingFlow('cart')
        break
      case 'checkout':
        await navigateShoppingFlow('checkout')
        break
    }
    
  } catch (error) {
    showError(`添加商品失败: ${error.message}`)
  }
}

// 🎮 快速购买流程
async function quickBuy(productId) {
  try {
    // 1. 跳转到商品详情
    await navigateShoppingFlow('detail', productId)
    
    // 2. 模拟添加到购物车
    await new Promise(resolve => setTimeout(resolve, 500))
    
    // 3. 直接进入结算流程
    await navigateShoppingFlow('checkout')
    
  } catch (error) {
    showError(`快速购买失败: ${error.message}`)
  }
}
</script>

<template>
  <div class="shopping-flow">
    <h3>🛍️ 智能购物导航</h3>
    
    <!-- 🎯 流程指示器 -->
    <div class="flow-indicator">
      <div
        v-for="(step, key) in flowSteps"
        :key="key"
        :class="['flow-step', { 
          active: shoppingFlow.currentStep === key,
          completed: isStepCompleted(key)
        }]"
        @click="navigateShoppingFlow(key)"
      >
        <div class="step-icon">{{ step.icon }}</div>
        <div class="step-title">{{ step.title }}</div>
      </div>
    </div>
    
    <!-- 🛒 购物车状态 -->
    <div class="cart-status">
      <div class="cart-info">
        <span class="cart-icon">🛒</span>
        <span class="cart-count">{{ shoppingFlow.cart.length }}</span>
        <span class="cart-text">件商品</span>
      </div>
      
      <button 
        @click="navigateShoppingFlow('cart')"
        class="btn btn-primary"
      >
        查看购物车
      </button>
    </div>
    
    <!-- 🎮 快捷操作 -->
    <div class="quick-actions">
      <h4>🚀 快捷操作</h4>
      
      <div class="action-grid">
        <button 
          @click="navigateShoppingFlow('browse')"
          class="action-btn"
        >
          <div class="action-icon">🛍️</div>
          <div class="action-text">浏览商品</div>
        </button>
        
        <button 
          @click="quickBuy('iphone-15')"
          class="action-btn"
        >
          <div class="action-icon">⚡</div>
          <div class="action-text">快速购买</div>
        </button>
        
        <button 
          @click="navigateShoppingFlow('checkout')"
          class="action-btn"
        >
          <div class="action-icon">💳</div>
          <div class="action-text">立即结算</div>
        </button>
        
        <button 
          @click="addToCartAndNavigate({ 
            id: 'demo-product', 
            name: '演示商品', 
            price: 99 
          })"
          class="action-btn"
        >
          <div class="action-icon">➕</div>
          <div class="action-text">添加商品</div>
        </button>
      </div>
    </div>
  </div>
</template>

<style scoped>
.shopping-flow {
  padding: 24px;
  background: white;
  border-radius: 16px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}

.flow-indicator {
  display: flex;
  justify-content: space-between;
  margin-bottom: 32px;
  padding: 20px;
  background: #f8fafc;
  border-radius: 12px;
  overflow-x: auto;
}

.flow-step {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 12px;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.3s ease;
  min-width: 80px;
}

.flow-step:hover {
  background: #e2e8f0;
}

.flow-step.active {
  background: #3b82f6;
  color: white;
}

.flow-step.completed {
  background: #10b981;
  color: white;
}

.step-icon {
  font-size: 24px;
  margin-bottom: 8px;
}

.step-title {
  font-size: 12px;
  text-align: center;
  font-weight: 500;
}

.cart-status {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border-radius: 12px;
  margin-bottom: 24px;
}

.cart-info {
  display: flex;
  align-items: center;
  gap: 8px;
}

.cart-icon {
  font-size: 24px;
}

.cart-count {
  font-size: 20px;
  font-weight: bold;
  background: rgba(255, 255, 255, 0.2);
  padding: 4px 12px;
  border-radius: 20px;
}

.quick-actions h4 {
  margin-bottom: 16px;
  color: #1f2937;
}

.action-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 16px;
}

.action-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px 16px;
  background: #f8fafc;
  border: 2px solid #e2e8f0;
  border-radius: 12px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.action-btn:hover {
  background: #e2e8f0;
  border-color: #cbd5e1;
  transform: translateY(-2px);
}

.action-icon {
  font-size: 32px;
  margin-bottom: 8px;
}

.action-text {
  font-size: 14px;
  font-weight: 500;
  color: #374151;
}

.btn {
  padding: 10px 20px;
  background: rgba(255, 255, 255, 0.2);
  border: none;
  border-radius: 8px;
  color: white;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.3s ease;
}

.btn:hover {
  background: rgba(255, 255, 255, 0.3);
}
</style>

🎯 最佳实践

✅ 推荐做法

  1. 🎯 使用命名路由

    javascript
    // ✅ 推荐 - 使用命名路由
    router.push({ name: 'user-profile', params: { id: userId } })
    
    // ❌ 不推荐 - 硬编码路径
    router.push(`/user/${userId}`)
  2. 🔄 处理导航 Promise

    javascript
    // ✅ 推荐 - 处理异步结果
    try {
      await router.push({ name: 'target-page' })
      console.log('导航成功')
    } catch (error) {
      console.error('导航失败:', error)
    }
    
    // ❌ 不推荐 - 忽略 Promise
    router.push({ name: 'target-page' })
  3. 🎮 合理使用 replace

    javascript
    // ✅ 表单提交后使用 replace
    router.replace({ name: 'success-page' })
    
    // ✅ 登录重定向使用 replace
    router.replace(redirectPath)

⚠️ 注意事项

  1. 参数与路径冲突 - params 不能与 path 同时使用
  2. 历史记录限制 - router.go() 超出范围会静默失败
  3. 导航守卫 - 可能会阻止导航执行
  4. 异步操作 - 记得处理 Promise 的成功和失败状态

🚀 下一步学习

掌握了编程式导航后,建议继续学习:

  1. 路由守卫 - 控制导航权限和流程
  2. 历史模式 - 了解不同的历史记录模式
  3. 路由懒加载 - 优化应用性能
  4. 导航故障 - 处理导航异常情况

🎉 恭喜!你已经掌握了编程式导航

现在你可以用代码精确控制用户的导航体验了!让路由跳转更智能、更流畅 🚀

🚀 Vue Router - 让前端路由变得简单而强大 | 构建现代化单页应用的最佳选择