vue-router的基本使用和配置
首先要有两个组件Home.vue和About.vue,之后将这两个组件注册到路由中:
import Home from '../pages/Home.vue';
import About from '../pages/About.vue';
// 配置映射关系
const routes = [
{path: '/', component: Home},
{path: '/home', component: Home},
{path: '/about', component: About}
];
创建一个路由对象,并传入配置:
import {createRouter, createWebHashHistory, createWebHistory} from 'vue-router'
// 创建一个路由对象router
const router = createRouter({
routes,
history: createWebHashHistory()
})
在main.js中使用router:
import { createApp } from 'vue'
import router from './router'
import App from './App.vue'
const app=createApp(App);
app.use(router);
app.mount('#app');
此时路由已经添加成功,需要在app.vue中使用路由组件。
router-view是用于显示路由中组件的地方,router-link是一个控制路由路径的a元素,使用如下:
<div>
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view></router-view>
</div>
vue-router知识点补充
在路由关系中:
const routes = [
{path: '/', component: Home},
{path: '/home', component: Home},
{path: '/about', component: About}
];
默认的’/’,可以为其指定一个component,也可以重定向到一个页面:
{path: '/', redirect: '/home'},
route还有两个属性:name是为一个route定义一个名字,路由是可以通过这个name跳转的;meta定义了这个路由的内部数据,可以通过route.meta来获取到。
对于route-link,默认使用的是history的popstate策略,为其增加replace属性后将使用replacestate策略。
<div>
<router-link to="/home" replace>首页</router-link>
<router-link to="/about" replace>关于</router-link>
<router-view></router-view>
</div>
被选中的route-link,会自动添加上两个class属性:router-link-active和router-link-exact-active

可以利用这个class为其配置样式。其中router-link-exact-active与router-link-active的区别是,router-link-exact-active只有在精准匹配时才会加上这个class(嵌套组件的嵌套路由),而router-link-active是任何父组件也会加上。
这个默认的class可以通过active-class和extra-active-class来修改:
<div>
<router-link to="/home" replace active-class="home-active">首页</router-link>
<router-link to="/about" replace active-class="about-active">关于</router-link>
<router-view></router-view>
</div>

路由懒加载
在路由配置的component属性中,将原来的组件替换为一个function(import的promise):
// import Home from '../pages/Home.vue';
// import About from '../pages/About.vue';
const routes = [
{path: '/', redirect: '/home'},
{path: '/home', component: ()=>{
return import("../pages/Home.vue")
}},
{path: '/about', component: ()=>{
return import("../pages/About.vue")
}}
];

利用webpack的魔法注释来为组件分包文件命名,格式为/* webpackChunkName: “” */:
const routes = [ {path: '/', redirect: '/home'}, {path: '/home', component: ()=>{ return import(/* webpackChunkName: "home-chunk" */"../pages/Home.vue") }}, {path: '/about', component: ()=>{ return import(/* webpackChunkName: "page-chunk" */"../pages/About.vue") }} ];

动态路由
在route中可以加上/:params来表示动态路由:
// route
{
path: "/user/:username",
component: ()=>{
return import("../pages/User.vue")
}
}
在route-link中,必须要匹配到对应的格式才能正确路由:
// route-link
<router-link :to="'/user/'+name">用户</router-link>
在组件中获取动态路由的参数:一是从this中获取,在this.$route中:
<template>
<div>
<h2>User: {{$route.params.username}}</h2>
</div>
</template>
<script>
export default {
created() {
console.log(this.$route.params.username);
},
}
</script>
二是使用vue-route 4的钩子函数useRoute直接获取这个组件的route:
<script>
import {useRoute, useRouter} from "vue-router"
export default {
setup(props) {
const route = useRoute();
console.log(route.params.username);
}
}
</script>
嵌套路由同理。
NotFound
定义一个类似于动态路由中的route,不过path是固定写法:
{
// 固定写法pathMatch
path: "/:pathMatch(.*)",
component: ()=>{
return import("../pages/NotFound.vue")
}
}
由此可以导向我们编写的NotFound的组件。
获取NotFound时的路径的方法:从route.params.pathMatch中获取:
<h2>{{$route.params.pathMatch}}</h2>
如果在/:pathMatch(.*)后面再加上一个*,就会将路径按照每一个”/”为分隔符进行分割并返回一个数组。
嵌套路由
现在home组件下还有两个子组件HomeShops和HomeMessage,分别应该使用/home/shops和/home/message来访问之,在配置路由时,在home的路由下的children属性来为这两个组件配置。
{
path: '/home',
component: ()=>{
return import(/* webpackChunkName: "home-chunk" */"../pages/Home.vue")
},
name: "home",
meta: {
name: "shopkeeper"
},
children: [
{
path:'shops',
component: ()=> import("../pages/HomeShops.vue")
},
{
path:'message',
component:()=> import("../pages/HomeMessage.vue")
}
]
},
可以发现在children中的route,path缺省了父组件的”/home”,并且也不需要加上”/”。
在children中设置重定向,path直接填写空字符串即可,但是redirect内容要填写完整的重定向路径:
children: [
{
path:"",
redirect:"/home/message"
},
{
path:'shops',
component: ()=> import("../pages/HomeShops.vue")
},
{
path:'message',
component:()=> import("../pages/HomeMessage.vue")
}
]
编程式导航
编程式导航是指通过代码来实现跳转逻辑。例如我们需要点击一个按钮来跳转到“关于”页,假设有如下的button:
<button @click="jumpToAbout">关于</button>
对于jumpToAbout方法,如果是在methods的options中定义,可以直接使用this.$router对象:
methods: {
jumpToAbout(){
// 首先要拿到router对象(不推荐直接导入)
this.$router.push("/about");
}
},
如果是在setup中,需要使用一个和useRoute相似的钩子函数useRouter:
import {useRouter} from 'vue-router'
export default {
components: {
},
setup(props) {
const router = useRouter();
function jumpToAbout(){
// 首先要拿到router对象(不推荐直接导入)
router.push("/about");
}
return {
name: "shopkeeper"
}
},
在这个push方法中还可以传入一个对象(router-link的to属性也可以传入对象),对象中可以包含其它的一些配置,例如:
router.push({
path:"/about",
query:{
name: "shopkeeper",
age:18
}
});
参数可以在子组件的$route.query中获取:
<h2>About: {{$route.query.name}}-{{$route.query.age}}</h2>
router还有的方法例如replace、go、forward、back等都和history中的用法相同。
router-link的v-slot
router-link实际上可以看作是一个插槽,在这个标签的内部可以写入我们想要展示的元素或者组件:
<router-link to="/home" replace active-class="home-active">
<nav-bar title="首页"></nav-bar>
</router-link>
route-link中定义v-slot属性,可以获取其内部传来的一个对象:
<!-- props:href 跳转的链接 -->
<!-- props:route对象 -->
<!-- props:navigate函数 -->
<!-- props:isActive 当前是否处于活跃状态 -->
<!-- props:isExactActive 是否是精确活跃状态 -->
<router-link to="/home" replace active-class="home-active" v-slot="props" custom>
<!-- <nav-bar title="首页"></nav-bar> -->
<button>{{props.href}}</button>
<p>{{props.route}}</p>
<button @click="props.navigate">导航</button>
<span :class="{'active': props.isActive}">{{props.isActive}}</span>
</router-link>
如果再加上custom属性的话,router-link将不会给插槽中的组件外层嵌套一个a标签,即此时跳转需要我们手动执行(使用props.navigate函数)。
router-view的v-slot
router-view也可以使用插槽,在v-slot中可以获取props.Component属性,即当前要显示的组件,通过这种方式就可以结合transition、component、keep-live等组件使用
动态添加/删除路由
对于一些需要选择性注释的路由,可以动态添加:
// 动态添加路由
const categoryRoute = {
path: "/category",
component: ()=>import("../pages/Category.vue")
}
const momentRoute = {
path: "moment",
component: ()=>import("../pages/HomeMoment.vue")
}
// 添加顶级的路由对象
router.addRoute(categoryRoute);
// 添加二级的路由对象
router.addRoute("home",momentRoute);
动态删除一个路由有三种方式:
- 添加一个name相同的路由,之前的路由就会被替代
- 通过removeRoute方法,传入路由的名称
- 通过addRoute方法的返回值回调(返回的值是一个删除路由的函数)
路由的其它一些方法补充:
- router.hasRoute() 检查路由是否存在
- router.getRoutes() 获取一个包含所有路由记录的数组
路由导航守卫
路由的导航守卫有多种函数可以实现,现在以beforeEach为例,beforeEach种传入一个函数:
// to:Route对象,即将跳转到的route对象
// from:Route对象,从哪个路由对象导航过来的
/**
* 返回值:
* 1.false:不进行导航
* 2.undefined或者不写返回值:进行默认的导航
* 3.字符串:路径,跳转到对应的路径中
* 4.对象:类似于router.push({path:"/login",component})
*/
router.beforeEach((to, from)=>{
console.log(to.path,from.path);
if (to.path.indexOf("/home")!==-1){
return "/login"
}
})
使用导航守卫实现一个简单的登录逻辑:假定login组件中,按下登录按钮就会在localStorage中保存一个token,
<template>
<div>
<button @click="loginClick">登录</button>
</div>
</template>
<script>
import {useRouter} from 'vue-router'
export default {
setup(props) {
const router=useRouter()
const loginClick=()=>{
window.localStorage.setItem("token","shopkeper")
router.push({
path:"/home"
})
}
return {loginClick}
}
}
</script>
那么在导航守卫中,如果要跳转到一个非login的页面,都应该判断本地是否有token,如果没有就应该跳转到login组件中:
router.beforeEach((to, from)=>{
if (to.path!== "/login"){
const token = window.localStorage.getItem("token");
if (!token){
return "/login"
}
}
})
其它的导航守卫可以参考官网