MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Vue Router 实际项目中的典型应用场景与案例分析

2023-12-244.8k 阅读

一、Vue Router 基础概述

Vue Router 是 Vue.js 官方的路由管理器,它与 Vue.js 核心深度集成,让构建单页面应用变得易如反掌。在单页面应用(SPA)中,Vue Router 负责管理页面的导航和视图的切换,通过 URL 的变化来加载不同的组件,提供了一种高效、灵活的页面管理方式。

(一)基本原理

Vue Router 的核心原理基于浏览器的 History API 和 Hash 模式。

  1. Hash 模式:在这种模式下,URL 中会带有 # 符号,例如 http://example.com/#/home# 后面的部分称为哈希值(hash),当哈希值发生变化时,浏览器不会向服务器发送请求,而是触发 hashchange 事件。Vue Router 正是利用这个事件来监听 URL 的变化,并根据哈希值匹配相应的路由。
  2. History 模式:History 模式使用 HTML5 的 History API 来管理 URL,它使得 URL 看起来更像传统的网站 URL,没有 # 符号,例如 http://example.com/home。通过 history.pushState()history.replaceState() 方法,我们可以在不刷新页面的情况下修改 URL,并且可以监听 popstate 事件来响应 URL 的变化。

(二)核心概念

  1. 路由配置:在 Vue Router 中,我们通过定义一个路由表来配置应用的路由。路由表是一个数组,每个元素是一个路由对象,包含 path(路由路径)和 component(对应的组件)等属性。例如:
const routes = [
  {
    path: '/home',
    component: HomeComponent
  },
  {
    path: '/about',
    component: AboutComponent
  }
];
  1. 路由匹配:当 URL 发生变化时,Vue Router 会根据路由表中的配置进行匹配。它会从根路径开始,按照顺序依次匹配路由,找到第一个匹配成功的路由,并渲染对应的组件。如果没有匹配到任何路由,可以通过定义一个通配符路由 * 来处理 404 页面。
  2. 路由导航:在 Vue 组件中,我们可以通过 $router 对象来进行路由导航。例如,使用 this.$router.push('/home') 可以跳转到 /home 路由对应的页面。还可以使用 this.$router.replace('/home') 进行替换式导航,这种导航不会留下历史记录,类似于重定向。

二、Vue Router 在实际项目中的典型应用场景

(一)页面级导航

  1. 多页面应用模拟:在实际项目中,我们经常需要构建具有多个页面的应用,Vue Router 可以通过不同的路由路径来模拟传统多页面应用的页面切换效果。比如一个电商网站,有首页、商品列表页、商品详情页、购物车页、个人中心页等。
const routes = [
  {
    path: '/',
    component: HomePage
  },
  {
    path: '/productList',
    component: ProductListPage
  },
  {
    path: '/product/:id',
    component: ProductDetailPage
  },
  {
    path: '/cart',
    component: CartPage
  },
  {
    path: '/userCenter',
    component: UserCenterPage
  }
];

在模板中,我们可以通过 <router - link> 组件来创建导航链接:

<router - link to="/">首页</router - link>
<router - link to="/productList">商品列表</router - link>
<router - link to="/cart">购物车</router - link>
<router - link to="/userCenter">个人中心</router - link>
  1. 导航栏切换:导航栏是页面级导航的重要组成部分。通过 Vue Router 可以轻松实现导航栏切换页面的功能。例如,在一个后台管理系统中,导航栏可能包含仪表盘、用户管理、订单管理等菜单项。
<template>
  <div>
    <ul class="nav">
      <li><router - link to="/dashboard">仪表盘</router - link></li>
      <li><router - link to="/userManagement">用户管理</router - link></li>
      <li><router - link to="/orderManagement">订单管理</router - link></li>
    </ul>
    <router - view></router - view>
  </div>
</template>

这里 <router - view> 是路由渲染的出口,会根据当前的 URL 渲染对应的组件。

(二)嵌套路由

  1. 复杂页面结构:有些页面具有复杂的结构,包含多个子部分,每个子部分又有自己的路由。以一个博客系统为例,文章详情页可能包含文章内容、评论区、相关文章推荐等子部分,并且每个子部分可能有不同的状态或操作,需要通过路由来管理。
const routes = [
  {
    path: '/article/:id',
    component: ArticleDetail,
    children: [
      {
        path: 'comments',
        component: ArticleComments
      },
      {
        path: 'related',
        component: RelatedArticles
      }
    ]
  }
];

ArticleDetail 组件的模板中,需要添加 <router - view> 来渲染子路由对应的组件:

<template>
  <div>
    <h1>{{ article.title }}</h1>
    <p>{{ article.content }}</p>
    <router - link to="/article/123/comments">查看评论</router - link>
    <router - link to="/article/123/related">查看相关文章</router - link>
    <router - view></router - view>
  </div>
</template>
  1. 多级嵌套路由:对于更复杂的应用,可能存在多级嵌套路由。比如在一个大型电商管理系统中,商品管理模块可能有商品分类管理、商品品牌管理等,而商品分类管理又可能有一级分类、二级分类等子模块,每个子模块都有自己的路由。
const routes = [
  {
    path: '/productManagement',
    component: ProductManagement,
    children: [
      {
        path: 'categoryManagement',
        component: CategoryManagement,
        children: [
          {
            path: 'firstLevel',
            component: FirstLevelCategory
          },
          {
            path: 'secondLevel',
            component: SecondLevelCategory
          }
        ]
      },
      {
        path: 'brandManagement',
        component: BrandManagement
      }
    ]
  }
];

在这种情况下,各级组件模板中都需要相应的 <router - view> 来渲染子路由组件。

(三)路由传参

  1. 动态路由参数:当我们需要根据不同的参数展示不同的内容时,动态路由参数非常有用。比如在商品详情页,我们需要根据商品的 ID 来展示具体商品的信息。
const routes = [
  {
    path: '/product/:id',
    component: ProductDetail
  }
];

ProductDetail 组件中,可以通过 this.$route.params.id 来获取商品 ID:

<template>
  <div>
    <h1>商品详情 - {{ $route.params.id }}</h1>
    <!-- 根据商品 ID 加载商品信息 -->
  </div>
</template>
  1. 查询参数:除了动态路由参数,还可以使用查询参数来传递信息。例如,在商品列表页,我们可能需要根据用户的筛选条件来展示商品列表,筛选条件可以通过查询参数传递。
<router - link :to="'/productList?category=electronics&priceRange=100 - 500'">电子产品列表(100 - 500 元)</router - link>

ProductList 组件中,可以通过 this.$route.query.categorythis.$route.query.priceRange 来获取筛选条件:

<template>
  <div>
    <h1>商品列表 - {{ $route.query.category }}</h1>
    <p>价格范围:{{ $route.query.priceRange }}</p>
    <!-- 根据筛选条件加载商品列表 -->
  </div>
</template>

(四)路由守卫

  1. 全局守卫:全局守卫可以在路由导航的过程中进行全局的控制,比如验证用户是否登录。可以通过 router.beforeEach 来定义全局前置守卫。
router.beforeEach((to, from, next) => {
  const isLoggedIn = localStorage.getItem('token');
  if (to.meta.requiresAuth &&!isLoggedIn) {
    next('/login');
  } else {
    next();
  }
});

在路由配置中,可以通过 meta 属性来标记需要登录才能访问的路由:

const routes = [
  {
    path: '/dashboard',
    component: Dashboard,
    meta: { requiresAuth: true }
  },
  {
    path: '/login',
    component: Login
  }
];
  1. 路由独享守卫:每个路由可以定义自己的独享守卫。例如,在某个特定的路由进入之前,需要进行一些特殊的权限验证。
const routes = [
  {
    path: '/admin/settings',
    component: AdminSettings,
    beforeEnter: (to, from, next) => {
      const hasAdminPermission = localStorage.getItem('role') === 'admin';
      if (hasAdminPermission) {
        next();
      } else {
        next('/403');
      }
    }
  }
];
  1. 组件内守卫:在组件内部也可以定义守卫,比如 beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave。例如,在一个编辑页面,当用户离开页面时,如果有未保存的更改,提示用户是否保存。
export default {
  beforeRouteLeave(to, from, next) {
    const hasUnsavedChanges = this.hasUnsavedChanges();
    if (hasUnsavedChanges) {
      const confirmLeave = window.confirm('你有未保存的更改,确定离开吗?');
      if (confirmLeave) {
        next();
      } else {
        next(false);
      }
    } else {
      next();
    }
  }
};

(五)懒加载路由组件

  1. 提高加载性能:在大型项目中,随着路由组件的增多,如果所有组件在应用启动时都加载,会导致首屏加载时间过长。Vue Router 支持懒加载路由组件,只有在路由被访问时才加载对应的组件。
const routes = [
  {
    path: '/home',
    component: () => import('./components/Home.vue')
  },
  {
    path: '/about',
    component: () => import('./components/About.vue')
  }
];

这里使用了 ES2020 的动态导入语法,Vue Router 会在需要时异步加载组件,从而提高应用的加载性能。

  1. 代码分割:懒加载不仅可以提高加载性能,还可以实现代码分割。将不同路由的组件分割成不同的代码块,使得应用的代码结构更加清晰,并且可以按需加载,减少初始加载的代码量。

三、Vue Router 案例分析

(一)电商项目案例

  1. 项目背景:我们要构建一个小型电商项目,包含首页、商品列表页、商品详情页、购物车页和结算页等主要页面。
  2. 路由配置
const routes = [
  {
    path: '/',
    component: HomePage
  },
  {
    path: '/productList',
    component: ProductListPage,
    children: [
      {
        path: 'category/:categoryId',
        component: CategoryProductList
      }
    ]
  },
  {
    path: '/product/:id',
    component: ProductDetailPage
  },
  {
    path: '/cart',
    component: CartPage
  },
  {
    path: '/checkout',
    component: CheckoutPage,
    meta: { requiresAuth: true }
  }
];
  1. 页面导航与传参:在首页,通过 <router - link> 导航到商品列表页,并可以传递分类 ID 作为查询参数:
<router - link :to="'/productList?category=electronics'">电子产品</router - link>
<router - link :to="'/productList?category=clothes'">服装</router - link>

在商品列表页,根据查询参数展示相应分类的商品列表:

<template>
  <div>
    <h1>商品列表 - {{ $route.query.category }}</h1>
    <ul>
      <li v - for="product in products" :key="product.id">
        <router - link :to="'/product/' + product.id">{{ product.name }}</router - link>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      products: []
    };
  },
  created() {
    const category = this.$route.query.category;
    // 根据分类 ID 从 API 获取商品列表
  }
};
</script>

在商品详情页,通过动态路由参数获取商品 ID 并展示商品详细信息:

<template>
  <div>
    <h1>{{ product.name }}</h1>
    <p>{{ product.description }}</p>
    <button @click="addToCart">加入购物车</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      product: {}
    };
  },
  created() {
    const productId = this.$route.params.id;
    // 根据商品 ID 从 API 获取商品详细信息
  },
  methods: {
    addToCart() {
      // 将商品添加到购物车逻辑
    }
  }
};
</script>
  1. 路由守卫应用:在结算页,我们使用全局守卫来验证用户是否登录。如果用户未登录,重定向到登录页。
router.beforeEach((to, from, next) => {
  const isLoggedIn = localStorage.getItem('token');
  if (to.meta.requiresAuth &&!isLoggedIn) {
    next('/login');
  } else {
    next();
  }
});
  1. 懒加载实现:为了提高项目的加载性能,我们对各个路由组件进行懒加载。
const routes = [
  {
    path: '/',
    component: () => import('./components/HomePage.vue')
  },
  {
    path: '/productList',
    component: () => import('./components/ProductListPage.vue'),
    children: [
      {
        path: 'category/:categoryId',
        component: () => import('./components/CategoryProductList.vue')
      }
    ]
  },
  {
    path: '/product/:id',
    component: () => import('./components/ProductDetailPage.vue')
  },
  {
    path: '/cart',
    component: () => import('./components/CartPage.vue')
  },
  {
    path: '/checkout',
    component: () => import('./components/CheckoutPage.vue'),
    meta: { requiresAuth: true }
  }
];

(二)后台管理系统案例

  1. 项目背景:构建一个后台管理系统,包含仪表盘、用户管理、订单管理、权限管理等模块。不同角色的用户具有不同的访问权限。
  2. 路由配置与嵌套路由
const routes = [
  {
    path: '/dashboard',
    component: Dashboard,
    meta: { requiresAuth: true }
  },
  {
    path: '/userManagement',
    component: UserManagement,
    meta: { requiresAuth: true, roles: ['admin', 'operator'] },
    children: [
      {
        path: 'list',
        component: UserList
      },
      {
        path: 'edit/:id',
        component: UserEdit
      }
    ]
  },
  {
    path: '/orderManagement',
    component: OrderManagement,
    meta: { requiresAuth: true, roles: ['admin', 'operator'] },
    children: [
      {
        path: 'list',
        component: OrderList
      },
      {
        path: 'detail/:id',
        component: OrderDetail
      }
    ]
  },
  {
    path: '/permissionManagement',
    component: PermissionManagement,
    meta: { requiresAuth: true, roles: ['admin'] }
  }
];
  1. 路由守卫与权限控制:在全局守卫中,根据用户的角色来判断是否有权限访问相应的路由。
router.beforeEach((to, from, next) => {
  const isLoggedIn = localStorage.getItem('token');
  const userRole = localStorage.getItem('role');
  if (to.meta.requiresAuth &&!isLoggedIn) {
    next('/login');
  } else if (to.meta.roles &&!to.meta.roles.includes(userRole)) {
    next('/403');
  } else {
    next();
  }
});
  1. 导航栏与页面切换:在后台管理系统的导航栏中,根据用户的权限动态显示菜单项。
<template>
  <div>
    <ul class="nav">
      <li v - if="hasPermission('/dashboard')"><router - link to="/dashboard">仪表盘</router - link></li>
      <li v - if="hasPermission('/userManagement')"><router - link to="/userManagement/list">用户管理</router - link></li>
      <li v - if="hasPermission('/orderManagement')"><router - link to="/orderManagement/list">订单管理</router - link></li>
      <li v - if="hasPermission('/permissionManagement')"><router - link to="/permissionManagement">权限管理</router - link></li>
    </ul>
    <router - view></router - view>
  </div>
</template>
<script>
export default {
  methods: {
    hasPermission(route) {
      const userRole = localStorage.getItem('role');
      const routeMeta = this.$router.options.routes.find(r => r.path === route).meta;
      return (!routeMeta.roles || routeMeta.roles.includes(userRole));
    }
  }
};
</script>

(三)单页应用博客案例

  1. 项目背景:打造一个单页应用博客,用户可以查看文章列表、文章详情,还可以进行文章分类浏览和标签筛选。
  2. 路由配置与参数传递
const routes = [
  {
    path: '/',
    component: ArticleList
  },
  {
    path: '/article/:id',
    component: ArticleDetail
  },
  {
    path: '/category/:category',
    component: CategoryArticleList
  },
  {
    path: '/tag/:tag',
    component: TagArticleList
  }
];

在文章列表页,可以通过查询参数进行排序等操作:

<router - link :to="'/articleList?sort=date'">按日期排序</router - link>
<router - link :to="'/articleList?sort=views'">按浏览量排序</router - link>

ArticleList 组件中,根据查询参数获取相应排序的文章列表:

<template>
  <div>
    <ul>
      <li v - for="article in articles" :key="article.id">
        <router - link :to="'/article/' + article.id">{{ article.title }}</router - link>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      articles: []
    };
  },
  created() {
    const sort = this.$route.query.sort;
    // 根据排序参数从 API 获取文章列表
  }
};
</script>
  1. 嵌套路由与组件交互:在文章详情页,可能有评论区和相关文章推荐,使用嵌套路由来管理。
const routes = [
  {
    path: '/article/:id',
    component: ArticleDetail,
    children: [
      {
        path: 'comments',
        component: ArticleComments
      },
      {
        path: 'related',
        component: RelatedArticles
      }
    ]
  }
];

ArticleDetail 组件模板中:

<template>
  <div>
    <h1>{{ article.title }}</h1>
    <p>{{ article.content }}</p>
    <router - link to="/article/123/comments">查看评论</router - link>
    <router - link to="/article/123/related">查看相关文章</router - link>
    <router - view></router - view>
  </div>
</template>
  1. 路由懒加载与性能优化:对博客的各个路由组件进行懒加载,提高加载性能。
const routes = [
  {
    path: '/',
    component: () => import('./components/ArticleList.vue')
  },
  {
    path: '/article/:id',
    component: () => import('./components/ArticleDetail.vue'),
    children: [
      {
        path: 'comments',
        component: () => import('./components/ArticleComments.vue')
      },
      {
        path: 'related',
        component: () => import('./components/RelatedArticles.vue')
      }
    ]
  },
  {
    path: '/category/:category',
    component: () => import('./components/CategoryArticleList.vue')
  },
  {
    path: '/tag/:tag',
    component: () => import('./components/TagArticleList.vue')
  }
];

通过以上对 Vue Router 在实际项目中的典型应用场景与案例分析,我们可以看到 Vue Router 在构建单页面应用时的强大功能和灵活性,合理运用它可以极大地提升应用的用户体验和开发效率。在实际开发中,应根据项目的具体需求和特点,选择合适的路由配置、传参方式、守卫机制以及懒加载策略,以打造出高质量的前端应用。