本内容为系列内容,全部内容请看我的vue教程分类
什么是路由
大家还记得我最开始提到的spa单页面吗,通过不同的路径显示不同的组件,这个就是通过router实现的
那么首先我们还需要引入一个 router文件
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.js"></script>
这里的话我们还是以上面的bilibili消息中心为示例,使用到刚刚的代码
还是先定义组件模板
<template id="writeback">
<div>
<div>回复我的</div>
<div>这是回复我的界面</div>
</div>
</template>
<template id="aite">
<div>
<div>@我的</div>
<div>这是@我的界面</div>
</div>
</template>
<template id="zan">
<div>
<div>收到的赞</div>
<div>这是收到赞的界面</div>
</div>
</template>
然后定义组件
const writeback = {
template: '#writeback'
}
const aite = {
template: '#aite'
}
const zan = {
template: '#zan'
}
基础使用
在组件那一节我们是通过动态组件来实现界面切换的,这里我们就使用路由来实现页面切换
这里我们实例化vue的时候,属性里面的 router传入一个 VueRouter 的实例,这个数组routes就是详细的路径和对应的组件信息,比如我们浏览器访问 www.lookroot.cn/writeback的时候,页面就展示上面定义好的 writeback组件
let vm = new Vue({
el: '#app',
data() {
},
router: new VueRouter({
routes: [
{
path: '/writeback',
component: writeback
},
{
path: '/aite',
component: aite
},
{
path: '/zan',
component: zan
},
],
})
})
那么原本的导航也要进行修改 这个 router-link就会在页面中渲染成a标签,作为导航
<div class="bili-leftnav">
<ul>
<li>
<router-link to="/writeback">回复我的</router-link>
</li>
<li>
<router-link to="/aite">@我的</router-link>
</li>
<li>
<router-link to="/zan">收到赞的</router-link>
</li>
</ul>
</div>
那么我们原本的选中效果是没有了的因为没有绑定点击事件,但是路由会给默认激活的导航加上一个class名为router-link-active
,我们简单编写一下
/* 当前显示路由的颜色 */
.bili-content .bili-leftnav li .router-link-active {
color: #2fbbea;
}
然后和动态组件一样要有个展示的容器吧,这里我们也给它加上过渡动画,这个router-view就是路由界面的展示容器和动态组件的component是一样的
<div class="bili-rightcontent">
<transition enter-active-class="animated fadeInDown" leave-active-class="animated fadeInUp"
mode="out-in" :duration="200">
<router-view></router-view>
</transition>
</div>
好的这就完成了改造了看看效果吧完全是可以的,注意看路径的变化
路由重定向
如果我们打开这个界面的时候,路径后面没有跟着具体的路径,界面就不会显示任何的组件,这肯定是不友好的
必须设置一个默认界面,我们咋路由配置文件里面增加一个路由信息
{
path: '/',
redirect: '/writeback',
},
这里的意思就是,默认为初始界面的时候,就重定向到 writeback这个路径,这样writeback就成了默认界面了
命名视图和命名路由
命名视图
如果我们页面中同时有两个
我们在添加一个 view,并且name为 common
<div class="bili-rightcontent">
<transition enter-active-class="animated fadeInDown" leave-active-class="animated fadeInUp"
mode="out-in" :duration="200">
<router-view></router-view>
</transition>
<router-view name="common"></router-view>
</div>
定义一个组件模板
<template id="vuefooter">
<div>
<h2>footer</h2>
</div>
</template>
定义这个组件
const vuefooter = {
template: '#vuefooter'
}
然后修改一下 writeback的路由配置,component就换成components了,并且默认的容器展示writeback组件,命名为common的容器展示 刚刚定义的这个vuefooter组件
{
path: '/writeback',//点击的路径
// 命名视图
components: {
default: writeback,
common: vuefooter
},
},
看看效果,是可以的,这就是命名视图
命名路由
我们给路由也起名字,这样方便使用,这里还是以 writeback为例
{
path: '/writeback',//点击的路径
// 命名视图
components: {
default: writeback,
common: vuefooter
},
// 命名路由
name: 'writeback',
},
修改它的导航链接,这样就完成了
<router-link :to="{name:'writeback'}">回复我的</router-link>
嵌套路由和路由传参
这个嵌套大家就知道了,肯定是路由中有路由,就像组件中有组件一样,又是套娃行为
来看下这个界面,1部分是导航,2部分是路由展示的组件同时相对于3部分又是一个导航,3部分是路由展示的组件,这就是一个很明显的路由嵌套,我们就来实现一下
首先我们要添加这个我的消息这个导航链接
<div class="bili-leftnav">
<ul>
<li>
<router-link :to="{name:'writeback'}">回复我的</router-link>
</li>
<li>
<router-link to="/aite">@我的</router-link>
</li>
<li>
<router-link to="/zan">收到赞的</router-link>
</li>
<li>
<router-link to="/mymsg">我的消息</router-link>
</li>
</ul>
</div>
然后定义第二部分和第三部分的组件
这个第二部分的组件模板,它里面首先是一个导航,这个导航我循环渲染出来的,模拟了十个用户聊天导航,并且是使用了 :to也就是绑定动态的路径,路径后面跟上了 index也就是把当前是第几个用户传递给第三部分这就是路由传参params方式
路由传参params方式
<template id="mymsg">
<div>
<div>消息</div>
<div>
<ul>
<ol v-for="index in 10" >
<router-link :to="'/mymsg/msgcontent/'+index">用户{{index}}</router-link>
</ol>
</ul>
<router-view></router-view>
</div>
</div>
</template>
定义第三部分组件模板
<template id="msgcontent">
<div>
这是和用户{{id}}具体的消息界面
</div>
</template>
定义这两个组件,为什么要使用 props来接受传值那?下面解释
//第二部分组件
const mymsg = {
template: '#mymsg',
}
//第三部分组件
const msgcontent = {
template: '#msgcontent',
props: ['index'],
}
嵌套路由
然后定义这个嵌套路由,这个 mymsg就是第二部分的路由配置,然后给它配置一个children这里面就是嵌套的路由配置了,首先path后面的 :id就是匹配我们上面定义的router-link传参的index,这个props: true就是将我们传递的index值,自动注册为第三部分组件的 props
// 嵌套路由
{
path: '/mymsg',
component: mymsg,
children: [{
path: 'msgcontent/:index',
component: msgcontent,
props: true
}]
}
看下效果,这就完成了
路由传参query方式
我们回到前面的第一部分的导航,修改一下这个我得消息的导航,使用?连接传递值
<router-link to="/mymsg?username=lookroot">我的消息</router-link>
如果我们不适用上面的props来接受传值该怎么做呢?
修改一下组件定义,created周期时使用this.$route.query
来接受query方式的传值,赋值给username
const mymsg = {
template: '#mymsg',
data() {
return {
username: ''
}
},
created() {
// query传值
this.username = this.$route.query.username
},
}
然后在组件模板中渲染这个数据
<template id="msgcontent">
<div>
这是和用户{{id}}具体的消息界面
</div>
</template>
看下效果是可以的
@
路由模式
路由有两种模式history和hash模式,默认也就是我们前面使用的模式是hash模式
这种模式就是在后面跟着一长串,不是很好看,如果追求美观就可以使用history模式
来实例一下
定义两个组件模板
<template id="page1">
<div>
page1
</div>
</template>
<template id="page2">
<div>
page2
</div>
</template>
定义这两个组件
const page1 = {
template: '#page1',
}
const page2 = {
template: '#page2',
}
实例化一个router,指定mode为history模式
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
redirect: '/page1',
},
{
path: '/page1',
component: page1,
name: 'page1',
},
{
path: '/page2',
component: page2,
name: 'page2',
},
],
});
在页面中定义导航,这里我们介绍另外一种路由跳转的方式编程式的导航
<div id="app">
<!-- <router-link to="/page1">page1</router-link> -->
<button @click="gotoPage('/page1')">page1</button>
<button @click="gotoPage('/page2')">page2</button>
<router-view></router-view>
</div>
那么这里都绑定上一个点击事件,然后传递一个地址字符串
编程式的导航
在当前组件(父组件)中编写一下这个点击事件,使用this.$router.push
进行路由跳转
methods: {
gotoPage(path) {
// 编程式的导航
this.$router.push(path)
}
},
运行一下查看效果,看下导航栏地址是不是就美观多了啊
但是这个模式有问题的,比如我们直接在浏览器中打开这个链接是会报错的
解决这个问题的方法需要后端处理官网文档
全局路由守卫
顾明思议,守卫嘛,就是拦截放行的效果
这里我们举一个登录的示例,但只是简单的举例哦,真实开发并不是这样做的
首先我们在vue的原型指向上挂载一个属性,用来记录用户是否登录
Vue.prototype.isLogin = false
然后编写,第三个组件 login,并添加一个login点击事件
<template id="login">
<div>
<button @click="login">login</button>
</div>
</template>
然后定义这个组件,并编写点击事件,点击就修改isLogin的值为 true,并使用this.$router.back返回上一个页面
const login = {
template: '#login',
methods: {
login() {
Vue.prototype.isLogin = true;
//返回的意思
this.$router.back();
}
},
}
然后配置路由信息
{
path: '/login',
component: login,
name: 'login'
},
好的定义全局守卫,记住要在定义router之后 to, from, next
也不用解释吧,语义化的,前置守卫顾名思义也就是执行之前会调用的,所以我们在这里进行判断用户是否登录,如果未登录,我们就挑战到 命名路由为login这个路由地址
// 全局的 前置守卫
router.beforeEach((to, from, next) => {
if (!Vue.prototype.isLogin) {
next({
name: 'login'
});
} else {
next();
}
})
效果就是我们点击切换路由的时候,如果没有登录,就跳转到登录页面,但是这里大家仔细想这样一个问题,我的login路由同样也会执行守卫,同样我也没有登录,然后他就会再次跳转,这样就成了死循环,怎么解决这个问题呢?
路由元信息
我们简单修改一下路由配置,我们在需要判断登录的路由中加上meta: { isLogin: true },表明当前路由是需要登录的
routes: [
{
path: '/',
redirect: '/page1',
meta: { isLogin: true }
},
{
path: '/page1',
component: page1,
name: 'page1',
meta: { isLogin: true }
},
{
path: '/page2',
component: page2,
name: 'page2',
meta: { isLogin: true }
},
{
path: '/login',
component: login,
name: 'login'
},
],
然后我们怎么在路由守卫中读取这个字段那?
我们修改一下这个守卫,matched是路由记录,那么我们就在要到达的路由中进行查找字段to.matched.some(item => item.meta.isLogin)
如果有这个字段的才进行判断
router.beforeEach((to, from, next) => {
// matched 路由记录
if (to.matched.some(item => item.meta.isLogin)) {
if (!Vue.prototype.isLogin) {
next({
name: 'login'
});
} else {
next();
}
} else {
// 执行下一个钩子
next();
}
})
然后我们在else里面执行 next();就是执行下一个钩子,也就是放行的意思
测试一下看看效果,我们先把mode改为hash方便测试,可以看到要先点击login才能进行访问
组件内的路由守卫
大家记得有些网页你点击后退或者其他,会弹出提示 是否确定离开吗?
我们来实践一下,修改一下 page1的组件定义,在beforeRouteLeave的钩子里面进行判断
const page1 = {
template: '#page1',
beforeRouteLeave(to, from, next) {
const result = window.confirm('确定离开页面吗?将不会保存')
if (result) {
next()
} else {
next(false)
}
}
}
那么还有其他更多的路由参数,这里就不一一讲解了