Vue Router
Vue Router 是 Vue 官方的客户端路由解决方案。
客户端路由的作用是在单页应用 (SPA) 中将浏览器的 URL 和用户看到的内容绑定起来。当用户在应用中浏览不同页面时,URL 会随之更新,但页面不需要从服务器重新加载。
Vue Router 基于 Vue 的组件系统构建,你可以通过配置路由来告诉 Vue Router 为每个 URL 路径显示哪些组件。
概念
- 一个(条)路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理
- 前端路由:key 是路径,value 是组件
- 后端路由:key 是路径,value 是函数;服务器根据请求路径找到匹配的函数来处理请求,返回响应数据
- 适用于单页面应用。整个应用只有一个完整的页面,点击导航链接不会刷新页面,只会做页面的局部更新,数据需要通过
ajax
获取 - VueRouter 的实例对象(new 出来的)就是一个路由器
基本使用
安装:pnpm i vue-router@4
插件
- 在
main.js
入口文件中
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
const app = createApp(App);
app.use(router);
router.isReady().then(() => {
app.mount("#app");
});
import { createRouter, createWebHistory } from "vue-router";
import HomeView from "../views/HomeView.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
name: "home",
component: HomeView,
},
{
path: "/about",
name: "about",
component: () => import("../views/AboutView.vue"),
},
],
});
export default router;
<script setup lang="ts">
import { RouterLink, RouterView } from "vue-router";
</script>
<template>
<header>
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</header>
<RouterView />
</template>
注
- 在组件中使用
<RouterLink>
标签实现路由的切换,该标签最终会被解析为<a>
标签;用to
属性指定路由跳转的目标组件,用active-class
属性指定当前组件被激活时使用的样式; - 使用
<RouterView>
标签指定路由组件的呈现位置
几个注意点
- 路由组件通常存放在
views or pages
文件夹,一般非路由组件通常存放在components
文件夹 - 建议路由组件使用
kebab-case
命名,非路由组件使用PascalCase
命名 - 路由组件通过
<RouterView>
渲染使用,非路由组件通过写组件标签使用 - 通过切换,“隐藏”了的路由组件,默认是失活
deactivated
的,需要的时候再去激活activated
- 每个组件都有一个
$route
属性,里面存储着自己的路由信息(route.path
route.params
等) - 每个组件都有一个
$router
属性,里面存储着整个应用唯一的 router
动态路由
例如,我们可能有一个 User 组件,它应该对所有用户进行渲染,但用户 ID 不同。在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,我们称之为路径参数 :
import User from "./User.vue";
// 这些都会传递给 `createRouter`
const routes = [
// 动态字段以冒号开始
{ path: "/users/:id", component: User },
];
路径参数 用冒号 :
表示。当一个路由被匹配时,它的 params 的值将在每个组件中以 route.params
的形式暴露出来。
<template>
<div>
<!-- 当前路由可以通过 $route 在模板中访问 -->
User {{ $route.params.id }}
</div>
</template>
嵌套路由
- 配置路由规则,使用
children
配置项配置子路由:
routes: [
{
path: "/home",
component: Home,
children: [
// 通过 children 配置子级路由
{
path: "news", // 此处不加斜杠
component: News,
},
],
},
];
- 跳转(要写完整路径):
<router-link to="/home/news">News</router-link>
路由守卫
- 作用:对路由组件进行权限控制,允许或阻止对某些组件的访问(写在路由器中);
- 不能直接创建并暴露路由器,需要先用一个变量 router 接收路由器,然后在暴露之前添加路由守卫;
- 在配置路由时,在元信息 mata 里面配置该路由是否需要权限校验的信息;
- 分类:全局守卫、独享守卫、组件内守卫;
全局守卫:写在路由器外面
const router = new VueRouter({
// 先用一个变量接收路由器,在其暴露之前添加守卫
routes: [
{
name: "guanyu",
path: "/about",
component: About,
meta: "{ isAuth: true }", // 配置是否需要授权
},
],
});
// 全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to, from, next) => {
if (to.meta.isAuth) {
// 判断当前路由是否需要进行权限控制
if (localStorage.getItem("school") === "atguigu") {
// 权限控制的具体规则
next(); // 放行
} else {
alert("学校名不对,暂无权限查看");
}
} else {
next();
}
});
// 全局后置守卫:初始化时执行、每次路由切换后执行(路径已经变过去了,常用于修改网页标题)
router.afterEach((to, from) => {
if (to.meta.title) {
document.title = to.meta.title; // 修改网页的 title
} else {
document.title = "vue_test";
}
});
export default router;
独享守卫:写在路由器配置项里面
- 只有
beforeEnter
一个配置项
export default {
name: "guanyu",
path: "/about",
component: About,
meta: "{ isAuth: false }", // 配置是否需要授权
beforeEnter(to, from, next) {
if (to.meta.isAuth) {
// 判断当前路由是否需要进行权限控制
if (localStorage.getItem("school") === "atguigu") {
next();
} else {
alert("暂无权限查看");
}
} else {
next();
}
},
};
组件内守卫:写在组件配置项里面
- 有
beforeRouteEnter
和beforeRouteLeave
两个配置项,进入守卫和离开守卫
export default {
name: "About",
beforeRouteEnter(to, from, next) {}, // 进入守卫:通过路由规则,进入该组件时被调用
beforeRouteLeave(to, from, next) {}, // 离开守卫:通过路由规则,离开该组件时被调用
};
路由钩子函数
- 全局守卫(全局路由器 router 身上的函数)
beforeEach
全局前置:每次路由切换之前和初始化的时候afterEach
全局后置:每次路由切换完成之后调用和初始化的时候(用来改网页 title)beforResolve
- 独享守卫(每条路由的配置项,与 name,path 等同级)
beforeEnter
- 组件内守卫(每个组件的配置项)
beforeRouteEnter
通过路由规则进入该组件时被调用beforeRouteUpdate
beforeRouteLeave
通过路由规则离开该组件时被调用
路由器工作模式
1️⃣ history
模式
优点:路径更加美观,不带有 #
,更接近传统的网站路径
缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有 404
错误
import { createRouter, createWebHistory } from "vue-router";
const router = createRouter({
history: createWebHistory(), // history模式
routes,
});
2️⃣ hash
模式
优点:兼容性更好,因为不需要服务器端处理路径
缺点:路径带有 #
不太美观,且在 SEO 优化方面相对较差
import { createRouter, createWebHistory } from "vue-router";
const router = createRouter({
history: createWebHashHistory(), // hash模式
routes,
});
to
的两种写法
<!-- 第一种:to 的字符串写法 (直接写完整的路径) -->
<router-link active-class="active" to="/home">主页</router-link>
<!-- 第二种:to 的对象写法 -->
<router-link active-class="active" :to="{ path: '/home' }">Home</router-link>
命名路由
给路由规则命名,添加 name
属性,可以简化路由跳转及传参
const routes: [
{ name: 'zhuye', path: '/home', component: Home },
{ name: 'xinwen', path: '/news', component: News },
{ name: 'guanyu', path: '/about', component: About }
]
跳转路由
<!--简化前:需要写完整的路径(to 的字符串写法) -->
<router-link to="/news/detail">跳转</router-link>
<!--简化后:直接通过名字跳转(to 的对象写法配合 name 属性) -->
<router-link :to="{ name: 'guanyu' }">跳转</router-link>
嵌套路由
编写 News
的子路由:Detail.vue
配置路由规则,使用 children
配置项
const router = createRouter({
history: createWebHistory(),
routes: [
{ name: "zhuye", path: "/home", component: Home },
{
name: "xinwen",
path: "/news",
component: News,
children: [
{
name: "xiang",
path: "detail",
component: Detail,
},
],
},
{ name: "guanyu", path: "/about", component: About },
],
});
export default router;
跳转路由(记得要加完整路径)
<router-link to="/news/detail">xxxx</router-link>
<!-- 或 -->
<router-link :to="{ path: '/news/detail' }">xxxx</router-link>
<!-- News.vue -->
<template>
<div class="news">
<!-- 导航区 -->
<nav class="news-list">
<RouterLink v-for="news in newsList" :key="news.id" :to="{ path: '/news/detail' }">
{{ news.name }}
</RouterLink>
</nav>
<!-- 展示区 -->
<div class="news-detail">
<RouterView />
</div>
</div>
</template>
路由传参
1️⃣ query 参数
传递参数
<!-- 跳转并携带 query 参数(to 的字符串写法) -->
<router-link to="/news/detail?a=1&b=2&content=欢迎你">
跳转
</router-link>
<!-- 跳转并携带 query 参数(to 的对象写法) -->
<RouterLink
:to="{
name: 'xiang', // 用 name 也可以跳转
path: '/news/detail',
query: {
id: news.id,
title: news.title,
content: news.content,
},
}"
>
{{ news.title }}
</RouterLink>
接收参数
import { useRoute } from "vue-router";
const route = useRoute(); // 拿到当前路径的路由信息对象,包含路由的路径、参数等
// 打印 query 参数
console.log(route.query);
2️⃣ params 参数
传递参数
<!-- 跳转并携带 params 参数(to 的字符串写法,可以用模板字符串使用变量) -->
<RouterLink :to="`/news/detail/${news.id}/${news.title}/${news.content}`">
{{ news.title }}
</RouterLink>
<!-- 跳转并携带 params 参数(to 的对象写法) -->
<RouterLink
:to="{
name: 'xiang',
params: {
id: news.id,
title: news.title,
content: news.title,
},
}"
>
{{ news.title }}
</RouterLink>
接收参数
import { useRoute } from "vue-router";
const route = useRoute();
// 打印 params 参数
console.log(route.params);
传递
params
参数时,若使用to
的对象写法,必须使用name
配置项,不能用path
。传递
params
参数时,需要提前在规则中占位
const routes: [
{
name: 'xinwen',
path: '/news',
component: News,
children: [
{
name: 'xiang',
path: 'detail/:id/:title/:content'
}
]
},
]
路由的 props 配置
让路由组件更方便的收到参数(可以将路由参数作为 props
传给组件)
1️⃣ 布尔值写法
把收到的每一组 params 参数,作为 props 传给 Detail 组件
当 props 设置为 true 时,route.params 将被设置为组件的 props
{
name:'xiang',
path:'detail/:id/:title/:content',
component:Detail,
props: true
}
2️⃣ 对象写法
把对象中的每一组 key-value 作为 props 传给 Detail 组件
当 props 是一个对象时,它将原样设置为组件 props。当 props 是静态的时候很有用
{
name:'xiang',
path:'detail/:id/:title/:content',
component:Detail,
props: { a: 1, b: 2, c: 3 },
}
3️⃣ 函数写法
把函数返回的对象中每一组 key-value 作为 props 传给 Detail 组件
你可以创建一个返回 props 的函数。这允许你将参数转换为其他类型,将静态值与基于路由的值相结合等等
{
name:'xiang',
path:'detail/:id/:title/:content',
component:Detail,
props (route) {
return route.query
}
}
replace 属性
控制路由跳转时操作浏览器历史记录的模式
浏览器的历史记录有两种写入方式:分别为 push
和 replace
push
是追加历史记录 (默认值)replace
是替换当前记录
开启 replace
模式
<RouterLink replace ...>News</RouterLink>
编程式导航
路由组件的两个重要的属性:$route
和 $router
变成了两个 hooks
import { useRoute, useRouter } from "vue-router";
const route = useRoute(); // 当前路由信息对象
const router = useRouter(); // 路由器实例
console.log(route.query);
console.log(route.parmas);
// push 和 replace函数参数中的语法和 router-link 的 to 相同,可以写字符串和对象
console.log(router.push);
console.log(router.replace);
重定向
将特定的路径,重新定向到已有路由
const route = {
path: "/",
redirect: "/home",
};