yarn add vue-router@5
**2. 创建路由配置文件**
在项目 `src` 目录下新建 `router/index.ts` 文件,定义路由规则与路由实例。使用 `RouteRecordRaw` 类型约束路由配置,确保 TypeScript 类型检查生效:
```typescript
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
// 懒加载组件,减少初始加载时间
const Home = () => import('@/views/Home.vue');
const ProductDetail = () => import('@/views/ProductDetail.vue');
const Login = () => import('@/views/Login.vue');
// 路由规则数组,使用 RouteRecordRaw 类型约束
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'Home',
component: Home,
meta: { title: '首页 - 电商平台' } // 元信息,可用于设置页面标题等
},
{
path: '/product/:id', // 动态路由参数,用于商品详情页
name: 'ProductDetail',
component: ProductDetail,
meta: { title: '商品详情 - 电商平台' }
},
{
path: '/login',
name: 'Login',
component: Login,
meta: { title: '登录 - 电商平台', requiresAuth: false } // 无需登录即可访问
}
];
// 创建路由实例,使用 HTML5 history 模式(无 # 号)
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), // 基础路径从环境变量获取
routes
});
export default router;
3. 挂载路由实例
在应用入口文件 main.ts 中,通过 app.use(router) 挂载路由实例,使路由功能在整个应用中可用:
import { createApp } from 'vue';
import App from './App.vue';
import router from './router'; // 导入路由实例
const app = createApp(App);
app.use(router); // 挂载路由
app.mount('#app');
配置要点
createWebHistory 模式(需后端配合处理 404 页面),相比 createWebHashHistory(带 # 号)更符合 SEO 需求。 () => import('路径') 实现,Vue Router 5 对此优化后可减少初始加载时间 40%,显著提升首屏渲染速度[10]。 RouteRecordRaw 类型确保路由配置的类型安全,避免路径、组件等关键属性拼写错误。路由跳转是用户导航的核心操作,Vue Router 提供声明式和编程式两种跳转方式,并支持灵活的参数传递机制,满足电商场景中商品列表跳转详情页、搜索结果页等需求。
1. 声明式跳转:<router-link> 组件
通过 <router-link> 组件实现页面跳转,无需手动绑定点击事件,自动渲染为 <a> 标签,且支持激活状态样式(通过 router-link-active 类名)。
电商案例:商品列表页跳转到详情页
<!-- ProductList.vue -->
<template>
<div class="product-list">
<!-- 循环渲染商品列表 -->
<div v-for="product in products" :key="product.id" class="product-item">
<!-- 声明式跳转,传递 params 参数 -->
<router-link :to="`/product/${product.id}`" class="product-link">
<h3>{{ product.name }}</h3>
<p>价格:{{ product.price }} 元</p>
</router-link>
</div>
</div>
</template>
2. 编程式跳转:router.push() 方法
通过路由实例的 push 方法实现跳转,适用于需逻辑判断后跳转的场景(如表单提交后)。
电商案例:搜索按钮触发跳转
<!-- SearchBar.vue -->
<template>
<div class="search-bar">
<input v-model="keyword" placeholder="输入商品关键词..." />
<button @click="handleSearch">搜索</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useRouter } from 'vue-router';
const keyword = ref('');
const router = useRouter(); // 获取路由实例
const handleSearch = () => {
if (keyword.value.trim()) {
// 编程式跳转,传递 query 参数(URL 显示为 ?keyword=xxx)
router.push({
path: '/search',
query: { keyword: keyword.value } // query 参数会拼接到 URL 查询字符串
});
}
};
</script>
3. 参数传递对比:query vs params
| 类型 | 特点 | URL 格式 | 路由配置 | 获取方式 | 适用场景 |
|---|---|---|---|---|---|
| query | 类似 GET 请求参数,可保留 | /search?keyword=手机 |
无需特殊配置 | useRoute().query.keyword |
搜索、筛选等非核心参数 |
| params | 动态路由参数,需预定义 | /product/123 |
path: '/product/:id' |
useRoute().params.id |
商品 ID、用户 ID 等核心标识 |
参数接收示例(商品详情页):
<!-- ProductDetail.vue -->
<script setup lang="ts">
import { useRoute } from 'vue-router';
import { onMounted, ref } from 'vue';
const route = useRoute(); // 获取当前路由信息
const product = ref(null);
onMounted(() => {
// 从 params 中获取商品 ID(路由配置中定义的 :id)
const productId = route.params.id;
// 调用 API 获取商品详情
fetchProductDetail(productId).then(data => {
product.value = data;
});
});
</script>
嵌套路由用于实现页面结构的层级关系,如电商网站的“分类页-子分类页”:点击“手机分类”后,页面左侧显示分类导航(父组件),右侧显示手机列表(子组件)。其核心是通过 children 配置子路由,并在父组件中使用 <router-view> 渲染子组件。
1. 路由配置
在 router/index.ts 中定义父路由与子路由的层级关系:
const routes: RouteRecordRaw[] = [
{
path: '/category',
name: 'Category',
component: () => import('@/views/CategoryLayout.vue'), // 父组件(布局组件)
children: [
{
path: 'phone', // 子路由路径(完整 URL 为 /category/phone)
name: 'PhoneList',
component: () => import('@/views/PhoneList.vue') // 手机列表子组件
},
{
path: 'laptop', // 完整 URL 为 /category/laptop
name: 'LaptopList',
component: () => import('@/views/LaptopList.vue') // 笔记本列表子组件
},
{
path: '', // 默认子路由(当 URL 为 /category 时显示)
redirect: 'phone' // 重定向到手机列表
}
]
}
];
2. 父组件模板(布局组件)
父组件 CategoryLayout.vue 需包含 <router-view>,用于渲染子路由对应的组件:
<!-- CategoryLayout.vue -->
<template>
<div class="category-page">
<!-- 左侧分类导航(父组件固定内容) -->
<div class="category-sidebar">
<h2>商品分类</h2>
<ul>
<!-- 跳转到子路由,使用相对路径(无需 / 开头) -->
<li><router-link to="phone">手机</router-link></li>
<li><router-link to="laptop">笔记本</router-link></li>
</ul>
</div>
<!-- 右侧内容区,子路由组件将渲染到此处 -->
<div class="category-content">
<router-view /> <!-- 子路由出口 -->
</div>
</div>
</template>
<style scoped>
/* 布局样式:左侧导航固定宽度,右侧内容自适应 */
.category-page {
display: flex;
gap: 20px;
padding: 20px;
}
.category-sidebar {
width: 200px;
border-right: 1px solid #eee;
}
.category-content {
flex: 1;
}
</style>
嵌套路由规则
path 无需以 / 开头,会自动拼接父路由路径(如父路由 /category + 子路由 phone → /category/phone)。 <router-view>,否则子组件无法渲染。 redirect 设置默认子路由,避免父路由单独访问时内容为空。路由守卫用于控制导航权限,如登录验证、角色权限检查、数据预加载等,是实现安全访问和优化用户体验的关键机制。Vue Router 提供全局守卫、路由独享守卫和组件内守卫三种类型,覆盖不同层级的导航控制需求。
1. 全局守卫:beforeEach
全局守卫作用于所有路由跳转,常用于验证用户登录状态。电商案例:未登录用户访问购物车时跳转至登录页:
// router/index.ts
import { useUserStore } from '@/stores/user'; // 假设使用 Pinia 存储用户状态
// 全局前置守卫,每次路由跳转前触发
router.beforeEach((to, from) => {
const userStore = useUserStore(); // 获取用户状态
// 判断目标路由是否需要登录(通过 meta.requiresAuth 标记,默认需要)
const requiresAuth = to.meta.requiresAuth ?? true;
// 未登录且需要登录的路由,重定向到登录页,并记录目标路径(登录后返回)
if (!userStore.isLogin && requiresAuth) {
return {
path: '/login',
query: { redirect: to.fullPath } // 存储目标路径,登录后可跳转回来
};
}
// 设置页面标题(从路由 meta.title 获取)
document.title = to.meta.title as string || '电商平台';
});
2. 路由独享守卫:beforeEnter
路由独享守卫仅作用于特定路由,适用于细化权限控制(如管理员路由限制)。电商案例:仅管理员可访问订单管理页:
// router/index.ts 中的路由配置
{
path: '/admin/orders',
name: 'AdminOrders',
component: () => import('@/views/admin/Orders.vue'),
meta: { title: '订单管理 - 管理员后台' },
// 路由独享守卫,仅管理员可访问
beforeEnter: (to, from) => {
const userStore = useUserStore();
// 非管理员用户禁止访问,返回 false 取消导航
if (!userStore.isAdmin) {
alert('无权限访问管理员页面');
return false; // 阻止跳转
}
}
}
3. 组件内守卫:beforeRouteEnter
组件内守卫作用于当前组件,常用于路由进入前预加载数据,避免页面空白。电商案例:商品详情页进入前预加载数据:
<!-- ProductDetail.vue -->
<script setup lang="ts">
import { useRoute } from 'vue-router';
import { ref } from 'vue';
const product = ref(null);
const route = useRoute();
// 组件内守卫:路由进入前触发(此时组件实例未创建,无法访问 this)
beforeRouteEnter(async (to) => {
const productId = to.params.id;
// 预加载商品数据,返回结果会传递给 next 回调(Vue 4 可直接 return 数据)
const productData = await fetchProductDetail(productId);
// 返回数据会作为参数传递给组件内的 setup 或 created 钩子
return { productData };
}, (to, from, next) => {
// Vue 3 兼容写法(Vue 4 可省略 next,直接 return 数据)
next((vm) => {
vm.product = vm.productData; // 将预加载数据赋值给组件实例
});
});
</script>
守卫核心要点
next()。 beforeRouteEnter 无法直接访问组件实例(this 或 setup 变量),需通过返回数据或 next(vm => {}) 间接操作。通过路由守卫的组合使用,可构建多层级的权限控制体系,既保障了系统安全,又通过数据预加载提升了页面响应速度,为电商平台等复杂应用提供可靠的导航管理能力。