Vue Router 用法详解与案例

核心功能详解

1. 路由守卫(Navigation Guards)

路由守卫是 Vue Router 最强大的功能之一,允许你在路由导航过程中进行控制。

类型和用法:

javascript 复制代码
// 全局前置守卫
router.beforeEach((to, from, next) => {
  // 在导航前执行逻辑
  next(); // 必须调用next()来继续导航
});

// 全局解析守卫
router.beforeResolve((to, from, next) => {
  // 在导航被确认之前调用
  next();
});

// 全局后置钩子
router.afterEach((to, from) => {
  // 导航完成后执行
});

// 路由独享守卫
const routes = [
  {
    path: '/admin',
    component: AdminPage,
    beforeEnter: (to, from, next) => {
      // 特定路由的守卫
      next();
    }
  }
];

// 组件内守卫
const UserProfile = {
  beforeRouteEnter(to, from, next) {
    // 组件创建前调用
    next(vm => {
      // 通过vm访问组件实例
    });
  },
  beforeRouteUpdate(to, from, next) {
    // 当前路由改变但组件被复用时
    this.fetchData(to.params.id);
    next();
  },
  beforeRouteLeave(to, from, next) {
    // 离开该组件时调用
    next();
  }
}

2. 动态路由匹配

动态路由允许根据URL参数动态匹配组件。

javascript 复制代码
const routes = [
  // 基本动态路由
  { path: '/user/:id', component: User },
  
  // 多段参数
  { path: '/user/:id/post/:postId', component: Post },
  
  // 可选参数
  { path: '/user/:id?', component: UserList },
  
  // 正则匹配
  { path: '/:category(blog|news|article)/:id', component: Content },
  
  // 通配符路由
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound }
];

3. 路由元信息(Meta Fields)

路由元信息允许你在路由上附加自定义数据。

javascript 复制代码
const routes = [
  {
    path: '/admin',
    component: AdminDashboard,
    meta: {
      requiresAuth: true,
      permissions: ['admin', 'superuser'],
      title: '管理控制台'
    }
  }
];

4. 路由懒加载

使用动态导入实现路由懒加载,优化应用性能。

javascript 复制代码
const routes = [
  {
    path: '/dashboard',
    component: () => import(/* webpackChunkName: "dashboard" */ '@/views/Dashboard.vue')
  },
  {
    path: '/settings',
    component: () => import(/* webpackChunkName: "settings" */ '@/views/Settings.vue')
  }
];

5. 嵌套路由

嵌套路由允许创建复杂的UI布局。

javascript 复制代码
const routes = [
  {
    path: '/user/:id',
    component: UserLayout,
    children: [
      {
        path: '',
        component: UserProfile
      },
      {
        path: 'posts',
        component: UserPosts
      },
      {
        path: 'settings',
        component: UserSettings
      }
    ]
  }
];

6. 滚动行为控制

自定义路由切换时的滚动位置。

javascript 复制代码
const router = createRouter({
  history: createWebHistory(),
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition;
    } else if (to.hash) {
      return {
        el: to.hash,
        behavior: 'smooth'
      };
    } else {
      return { top: 0, behavior: 'smooth' };
    }
  }
});

7. 路由过渡效果

使用Vue的Transition组件实现路由切换动画。

vue 复制代码
<template>
  <router-view v-slot="{ Component }">
    <transition name="fade" mode="out-in">
      <component :is="Component" />
    </transition>
  </router-view>
</template>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>

实战案例:电子商务平台路由系统

下面是一个完整的电子商务平台路由实现:

javascript 复制代码
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import { useAuthStore } from '@/stores/auth';

// 路由懒加载
const HomePage = () => import('@/views/HomePage.vue');
const ProductList = () => import('@/views/products/ProductList.vue');
const ProductDetail = () => import('@/views/products/ProductDetail.vue');
const ShoppingCart = () => import('@/views/cart/ShoppingCart.vue');
const CheckoutProcess = () => import('@/views/checkout/CheckoutProcess.vue');
const UserProfile = () => import('@/views/user/UserProfile.vue');
const AdminDashboard = () => import('@/views/admin/Dashboard.vue');
const LoginPage = () => import('@/views/auth/LoginPage.vue');
const NotFound = () => import('@/views/errors/NotFound.vue');

const routes = [
  {
    path: '/',
    name: 'home',
    component: HomePage,
    meta: { title: '首页' }
  },
  {
    path: '/products',
    name: 'products',
    component: ProductList,
    meta: { title: '产品列表' }
  },
  {
    path: '/products/:id(\\d+)', // 只匹配数字ID
    name: 'product-detail',
    component: ProductDetail,
    props: true,
    meta: { title: '产品详情' }
  },
  {
    path: '/cart',
    name: 'cart',
    component: ShoppingCart,
    meta: { 
      title: '购物车',
      requiresAuth: true 
    }
  },
  {
    path: '/checkout',
    name: 'checkout',
    component: CheckoutProcess,
    meta: { 
      title: '结算',
      requiresAuth: true,
      disableLeave: true // 防止意外离开
    }
  },
  {
    path: '/profile',
    name: 'profile',
    component: UserProfile,
    meta: { 
      title: '个人资料',
      requiresAuth: true 
    }
  },
  {
    path: '/admin',
    name: 'admin',
    component: AdminDashboard,
    meta: { 
      title: '管理面板',
      requiresAuth: true,
      requiresAdmin: true 
    }
  },
  {
    path: '/login',
    name: 'login',
    component: LoginPage,
    meta: { 
      title: '登录',
      hideForAuth: true // 已登录用户隐藏
    }
  },
  {
    path: '/:pathMatch(.*)*',
    name: 'not-found',
    component: NotFound,
    meta: { title: '页面未找到' }
  }
];

const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior() {
    return { top: 0 };
  }
});

// 全局前置守卫
router.beforeEach(async (to, from, next) => {
  const authStore = useAuthStore();
  
  // 设置页面标题
  document.title = to.meta.title ? `${to.meta.title} - 电商平台` : '电商平台';
  
  // 检查是否需要登录
  if (to.meta.requiresAuth && !authStore.isAuthenticated) {
    next({ name: 'login', query: { redirect: to.fullPath } });
    return;
  }
  
  // 检查是否已登录但访问登录页面
  if (to.meta.hideForAuth && authStore.isAuthenticated) {
    next({ name: 'home' });
    return;
  }
  
  // 检查管理员权限
  if (to.meta.requiresAdmin && !authStore.isAdmin) {
    next({ name: 'forbidden' }); // 应创建一个403页面
    return;
  }
  
  // 继续导航
  next();
});

// 全局后置钩子
router.afterEach((to) => {
  // 页面访问统计
  trackPageView(to.fullPath);
});

export default router;

支付页面组件内守卫实现

vue 复制代码
<!-- CheckoutProcess.vue -->
<script>
export default {
  data() {
    return {
      isProcessing: false,
      hasUnsavedChanges: true
    };
  },
  
  beforeRouteLeave(to, from, next) {
    // 支付处理中禁止离开
    if (this.isProcessing) {
      alert('支付处理中,请稍候...');
      next(false);
      return;
    }
    
    // 未完成支付提示
    if (this.hasUnsavedChanges && to.name !== 'payment-success') {
      const confirmLeave = confirm(
        '您有未完成的支付,确定要离开吗?'
      );
      next(confirmLeave);
    } else {
      next();
    }
  },
  
  methods: {
    async processPayment() {
      this.isProcessing = true;
      try {
        await this.$store.dispatch('checkout/processPayment');
        this.hasUnsavedChanges = false;
        this.$router.push({ name: 'payment-success' });
      } catch (error) {
        alert(`支付失败: ${error.message}`);
      } finally {
        this.isProcessing = false;
      }
    }
  }
};
</script>

路由元信息与面包屑导航实现

vue 复制代码
<!-- Breadcrumb.vue -->
<template>
  <nav class="breadcrumb">
    <router-link to="/">首页</router-link>
    <template v-for="(crumb, index) in crumbs" :key="crumb.path">
      <span class="separator">/</span>
      <router-link 
        v-if="index < crumbs.length - 1"
        :to="crumb.path"
      >
        {{ crumb.meta.title }}
      </router-link>
      <span v-else class="current">
        {{ crumb.meta.title }}
      </span>
    </template>
  </nav>
</template>

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

export default {
  setup() {
    const route = useRoute();
    
    const crumbs = computed(() => {
      const matched = route.matched.filter(
        record => record.meta && record.meta.title
      );
      
      // 添加首页
      if (matched.length > 0 && matched[0].path !== '/') {
        matched.unshift({ path: '/', meta: { title: '首页' } });
      }
      
      return matched;
    });
    
    return { crumbs };
  }
};
</script>

路由模式与技巧

1. 基于角色的动态路由

javascript 复制代码
function generateRoutes(userRole) {
  const baseRoutes = [
    { path: '/', component: HomePage },
    { path: '/products', component: ProductList }
  ];
  
  const roleBasedRoutes = [];
  
  if (userRole === 'admin') {
    roleBasedRoutes.push(
      { path: '/admin', component: AdminDashboard },
      { path: '/analytics', component: AnalyticsDashboard }
    );
  }
  
  if (userRole === 'customer') {
    roleBasedRoutes.push(
      { path: '/profile', component: UserProfile },
      { path: '/orders', component: UserOrders }
    );
  }
  
  return [...baseRoutes, ...roleBasedRoutes];
}

// 在登录后动态添加路由
authStore.login().then(user => {
  const routes = generateRoutes(user.role);
  routes.forEach(route => {
    router.addRoute(route);
  });
});

2. 路由数据预取

javascript 复制代码
// 在路由配置中
{
  path: '/product/:id',
  component: ProductDetail,
  props: true,
  meta: {
    prefetch: async (route) => {
      // 预取产品数据
      const productId = route.params.id;
      await store.dispatch('products/prefetchProduct', productId);
    }
  }
}

// 在全局前置守卫中
router.beforeEach(async (to, from, next) => {
  if (to.meta.prefetch) {
    try {
      await to.meta.prefetch(to);
    } catch (error) {
      console.error('Prefetch failed:', error);
    }
  }
  next();
});

3. 路由过渡效果

vue 复制代码
<template>
  <router-view v-slot="{ Component, route }">
    <transition :name="route.meta.transition || 'fade'">
      <component :is="Component" :key="route.fullPath" />
    </transition>
  </router-view>
</template>

<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,
.slide-right-enter-active,
.slide-right-leave-active {
  transition: transform 0.4s ease;
}
.slide-left-enter-from {
  transform: translateX(100%);
}
.slide-left-leave-to {
  transform: translateX(-100%);
}
.slide-right-enter-from {
  transform: translateX(-100%);
}
.slide-right-leave-to {
  transform: translateX(100%);
}
</style>

<!-- 在路由配置中使用 -->
{
  path: '/products',
  component: ProductList,
  meta: { 
    title: '产品列表',
    transition: 'slide-right' 
  }
}

总结

Vue Router 的高级功能为构建复杂单页面应用提供了强大支持:

  1. 路由守卫:实现导航控制、权限验证和数据预加载
  2. 动态路由:处理参数化URL和复杂匹配场景
  3. 路由元信息:附加自定义数据用于权限控制、布局配置等
  4. 路由懒加载:优化应用性能,减少初始加载时间
  5. 嵌套路由:创建复杂UI布局和层次结构
  6. 滚动控制:提供更好的用户体验
  7. 路由过渡:增强页面切换的视觉效果
相关推荐
Jackson_Mseven1 分钟前
🧺 Monorepo 是什么?一锅端的大杂烩式开发幸福生活
前端·javascript·架构
我想说一句10 分钟前
JavaScript数组:轻松愉快地玩透它
前端·javascript
binggg12 分钟前
AI 编程不靠运气,Kiro Spec 工作流复刻全攻略
前端·claude·cursor
ye空也晴朗20 分钟前
基于eggjs+mongodb实现后端服务
前端
慕尘_24 分钟前
对于未来技术的猜想:Manus as a Service
前端·后端
爱学习的茄子29 分钟前
JS数组高级指北:从V8底层到API骚操作,一次性讲透!
前端·javascript·深度学习
小玉子30 分钟前
webpack未转译第三方依赖axios为es5导致低端机型功能异常
前端
爱编程的喵32 分钟前
React状态管理:从useState到useReducer的完美进阶
前端·react.js
markyankee10135 分钟前
Vue-Router:构建现代化单页面应用的路由引擎
前端·vue.js
Java水解35 分钟前
Spring WebFlux 与 WebClient 使用指南
前端