vue-router
1、理解
vue-router是vue的一个插件库,专门用来实现SPA应用
2、SPA应用
单页面Web应用(single page web application)
这个应用中只有一个完整的页面
点击页面中的导航连接不会刷新页面,只会做页面的局部更新
数据需要通过ajax请求获取
3、路由的理解
什么是路由
一个路由就是一组映射关系(key - value)
key就是路径,value可能是function或是component
路由的分类
后端路由
理解:value是function,用于处理客户端提交的请求
工作过程:服务器收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回相应数据
前端路由
理解:value是component,用于展示页面的内容
工作过程:当浏览器的路径改变时,对应的组件就会显示
4、基本使用
安装插件:npm i vue-router
引入插件:import VueRouter from 'vue-router'
应用插件:Vue.use(VueRouter)
// 引入vue
import Vue from "vue";
// 引入app
import App from "./App.vue";
// 引入VueRouter
import VueRouter from "vue-router";
// 引入路由器
import router from './router'
// 关闭生产提示
Vue.config.productionTip = false;
// 使用VueRouter
Vue.use(VueRouter);
// 创建vm
new Vue({
el:'#app',
render:h => h(App),
router:router
})
编写router配置项:
// 这个文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";
// 引入组件
import About from '../components/About'
import Home from '../components/Home'
// 创建并暴露一个路由器
export default new VueRouter({
// 注意这里是routes
routes:[
// 每一对花括号就是一组路由规则
{
path:'/about',
component:About,
},
{
path:'/home',
component:Home,
},
]
});
实现路由的切换
使用active-class用作样式的切换,在这个组件显示的时候就是这个属性的值的样式
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
指定展示位置
<div class="panel-body">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
注意事项
路由组件通常存放在pages文件夹中,一般组件通常存放在components文件夹中
通过切换,隐藏了的路由组件,默认是被销毁了,需要的时候再次挂载,所以点击切换页面组件的时候其实就是一个先销毁前一个组件,然后再挂载要显示的组件,无限重复,如果是初始页面就都不挂载,第一次显示组件的时候就先挂载,然后显示另一个组件的时候就先把第一次挂载的组件销毁,然后挂载第二个组件
每个组件都有自己的$routr属性,里面存储着自己组件的路由信息
整个应用就只有一个router,可以通过组件的$router属性获取到
5、嵌套(多级)路由
配置路由规则,使用children配置项
// 这个文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";
// 引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
// 创建并暴露一个路由器
export default new VueRouter({
// 注意这里是routes
routes:[
// 每一对花括号就是一组路由规则
// 这是一级路由
{
path:'/about',
component:About,
},
{
path:'/home',
component:Home,
// children 配置子级路由
children:[
// 这是二级路由,二级路由需要配置在一级路由里面
{
// 注意这里的路径不需要写‘/’,否则会报错或是不显示
path:'news',
component:News,
},
{
path:'message',
component:Message,
},
]
},
]
});
跳转,需要写完整的跳转路径,包括一级路径
<ul class="nav nav-tabs">
<li>
<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
</li>
<li>
<router-link class="list-group-item " active-class="active" to="/home/message">Message</router-link>
</li>
</ul>
6、路由传参-query参数
传递参数
<template>
<div>
<ul>
<li v-for="msg in messageList" :key="msg.id">
<!-- 跳转路由并携带query参数,to的字符串写法 -->
<!-- <router-link :to="`/home/message/showMessage?id=${msg.id}&title=${msg.title}`">{{msg.title}}</router-link> -->
<!-- 跳转路由并携带query参数,to的对象写法 -->
<router-link :to="{
path:'/home/message/showMessage',
query:{
id:msg.id,
title:msg.title
}
}">
{{msg.title}}
</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
接收参数
<template>
<ul>
<!-- 发送参数的组件所发送的数据,可以通过$route.query.参数名获取 -->
<li>消息编号:{{$route.query.id}}</li>
<li>消息标题:{{$route.query.title}}</li>
</ul>
</template>
7、命名路由
作用:可以简化路由的跳转
使用:
给路由命名
// 这个文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";
// 引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import ShowMessage from '../pages/ShowMessage'
// 创建并暴露一个路由器
export default new VueRouter({
// 注意这里是routes
routes:[
// 每一对花括号就是一组路由规则
{
path:'/about',
component:About,
},
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News,
},
{
path:'message',
component:Message,
children:[
{
// 用于简化路径
name:'showMessage',
path:'showMessage',
component:ShowMessage,
}
]
},
]
},
]
});
简化跳转
<template>
<div>
<ul>
<li v-for="msg in messageList" :key="msg.id">
<!-- 跳转路由并携带query参数,to的字符串写法 -->
<!-- <router-link :to="`/home/message/showMessage?id=${msg.id}&title=${msg.title}`">{{msg.title}}</router-link> -->
<!-- 跳转路由并携带query参数,to的对象写法 -->
<router-link :to="{
<!-- 不用写path路径了,,要写name属性 -->
name:'showMessage',
query:{
id:msg.id,
title:msg.title
}
}">
{{msg.title}}
</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
<router-link class="list-group-item" active-class="active" :to="{name:'guanyu'}">About</router-link>
8、路由传参-params参数
配置路由,声明接收params参数
// 这个文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";
// 引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import ShowMessage from '../pages/ShowMessage'
// 创建并暴露一个路由器
export default new VueRouter({
// 注意这里是routes
routes:[
// 每一对花括号就是一组路由规则
{
name:'guanyu',
path:'/about',
component:About,
},
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News,
},
{
path:'message',
component:Message,
children:[
{
name:'showMessage',
// 在path属性中的路径最后使用占位符声明接收params参数
// 格式 /:参数名/:参数名
path:'showMessage/:id/:title',
component:ShowMessage,
}
]
},
]
},
]
});
传递参数
<template>
<div>
<ul>
<li v-for="msg in messageList" :key="msg.id">
<!-- 跳转路由并携带params参数,to的字符串写法 -->
<!-- <router-link :to="`/home/message/showMessage/${msg.id}/${msg.title}`">{{msg.title}}</router-link> -->
<!-- 跳转路由并携带params参数,to的对象写法 -->
<!-- 使用psrams参数传递数据时,不能使用path参数,必须使用name属性 -->
<router-link :to="{
name:'showMessage',
params: {
id:msg.id,
title:msg.title
}
}">
{{msg.title}}
</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
注意:路由携带params参数时,如果使用to的对象写法,则不能使用path配置项,必须使用name配置项
接收参数
<template>
<ul>
<li>消息编号:{{$route.params.id}}</li>
<li>消息标题:{{$route.params.title}}</li>
</ul>
</template>
9、路由的props配置项
作用:让路由组件更方便的收到参数
// 这个文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";
// 引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import ShowMessage from '../pages/ShowMessage'
// 创建并暴露一个路由器
export default new VueRouter({
// 注意这里是routes
routes:[
// 每一对花括号就是一组路由规则
{
name:'guanyu',
path:'/about',
component:About,
},
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News,
},
{
path:'message',
component:Message,
children:[
{
name:'showMessage',
// 在path属性中的路径最后使用占位符声明接收params参数
// 格式 /:参数名/:参数名
// path:'showMessage/:id/:title',
// 注意:使用props配置的第三种方式时,如果接收的时query传递的参数,不能使用占位符声明接收参数
path:'showMessage',
component:ShowMessage,
// props配置的第一种写法:值为对象,这个对象中所有的key-value都会以props的形式传递给ShowMessage组件
// props:{
// a:1,
// b:'hello',
// }
// props配置的第二种写法:值为布尔值,如果布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传递给ShowMessage组件
// props:true,
// props配置的第三种写法:值为函数,这个函数返回的对象中每一组key-value都会通过props传给ShowMessage
props({query}){
return {
id:query.id,
title:query.title,
};
}
}
]
},
]
},
]
});
10、< router-link>的replace属性
作用:控制路由跳转时操作浏览器历史记录的模式
浏览器的历史记录有两种写入方式:
push:追加历史记录
replace:替换当前记录(只能替换一条记录)
默认为push
如何开启replace模式:
<router-link replace class="list-group-item" active-class="active" :to="{name:'guanyu'}">About</router-link>
11、编程式路由导航
作用:不借助实现路由跳转,让路由跳转更加灵活
示例:
<template>
<div>
<ul>
<li v-for="msg in messageList" :key="msg.id">
<!-- 跳转路由并携带params参数,to的字符串写法 -->
<!-- <router-link :to="`/home/message/showMessage/${msg.id}/${msg.title}`">{{msg.title}}</router-link> -->
<!-- 跳转路由并携带params参数,to的对象写法 -->
<!-- 使用params参数传递数据时,不能使用path参数,必须使用name属性 -->
<router-link :to="{
name:'showMessage',
query: {
id:msg.id,
title:msg.title
}
}">
{{msg.title}}
</router-link>
<button @click="pushShow(msg)">push查看</button>
<button @click="replaceShow(msg)">replace查看</button>
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'Message',
data(){
return {
messageList:[
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'},
]
}
},
methods:{
//给按钮绑定一个事件,然后在事件回调函数中调用$router中的方法,进行相应的操作
// 可以把除了a链接的其它标签也添加上路由跳转,并且其中参数和<router-link>中的一样
//借助$router的两个API
pushShow(msg){
this.$router.push({
name:'showMessage',
query: {
id:msg.id,
title:msg.title
}
})
},
replaceShow(msg){
this.$router.replace({
name:'showMessage',
query: {
id:msg.id,
title:msg.title
}
})
},
}
}
</script>
关于历史记录跳转的API:
<template>
<div>
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header">
<h2>Vue Router Demo</h2>
<button @click="back">后退</button>
<button @click="forward">前进</button>
<button @click="go">测试一下go</button>
</div>
</div>
</div>
</template>
<script>
export default {
name:'Banner',
methods:{
back(){
// 控制历史记录的后退
this.$router.back();
},
forward(){
// 控制历史记录的前进
this.$router.forward();
},
go(){
// 控制历史记录的前进与后退
// 正数就是前进、负数就是后退,数字就是前进几个页面或是后退几个页面
this.$router.go(2);
},
},
}
</script>
12、缓存路由组件
作用:让不展示路由组件保持挂载状态,不会被销毁
使用:把放入到标签中,最好加上include属性,如果不加这个属性,就会把这个标签中的所有路由组件都缓存,没必要,所以只把需要缓存的路由组件的组件名添加到include属性中当作属性值,就会在路由跳转的时候缓存这个组件的内容,不会被销毁,其它不在include属性中的路由组件会被销毁
注意:
当一个路由组件被缓存了以后,如果跳转到了这个组件所在的父组件以外的组件中时,缓存会被清空,组件会被销毁
要缓存多个路由组件的时候需要写为数组形式,然后include前加“:”,如:
如下:News组件被缓存了,被渲染到了Home组件中的标签中的标签中时,如果这时点击了Home组件以外的组件,重新渲染了Home组件,那么Home组件就会被销毁,所以News组件的缓存也就没了,也被销毁了
<template>
<div class="panel-body">
<div>
<h2>Home组件内容</h2>
<div>
<ul class="nav nav-tabs">
<li>
<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
</li>
<li>
<router-link class="list-group-item " active-class="active" to="/home/message">Message</router-link>
</li>
</ul>
<!-- 缓存一个路由组件 -->
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
<!-- 缓存多个路由组件 -->
<!-- <keep-alive :include="['News','Message']">
<router-view></router-view>
</keep-alive> -->
</div>
</div>
</div>
</template>
<script>
export default {
name:'Home',
}
</script>
12、两个新的生命周期钩子
作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态
两个钩子:
activated:当路由组件被激活时触发,也就是当组件被看到时
deactivated:当路由组件失活时触发,就是组件不被看到时
<template>
<ul>
<li :style="{opacity}">欢迎学习Vue</li>
<li>news001 <input type="text"></li>
<li>news002 <input type="text"></li>
<li>news003 <input type="text"></li>
</ul>
</template>
<script>
export default {
name:'News',
data(){
return{
opacity:1
}
},
// mounted(){
// this.timer = setInterval(() => {
// this.opacity -= 0.01
// if(this.opacity <= 0 ){
// this.opacity = 1;
// }
// }, 16)
// },
// beforeDestroy(){
// // console.log('News销毁了');
// clearInterval(this.timer)
// },
activated(){
this.timer = setInterval(() => {
this.opacity -= 0.01
if(this.opacity <= 0 ){
this.opacity = 1;
}
}, 16)
},
deactivated(){
clearInterval(this.timer)
},
}
</script>
13、路由守卫
作用:对路由进行权限控制
分类:全局守卫、独享守卫、组件内守卫
全局守卫:
// 这个文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";
// 引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import ShowMessage from '../pages/ShowMessage'
// 创建并暴露一个路由器
const router = new VueRouter({
// 注意这里是routes
routes:[
// 每一对花括号就是一组路由规则
{
name:'guanyu',
path:'/about',
component:About,
meta:{isAuth:false,title:'关于'}
},
{
name:'zhuye',
path:'/home',
component:Home,
meta:{isAuth:false,title:'主页'},
children:[
{
name:'xinwen',
path:'news',
component:News,
meta:{isAuth:true,title:'新闻'}
},
{
name:'xiaoxi',
path:'message',
component:Message,
meta:{isAuth:true,title:'消息'},
children:[
{
meta:{isAuth:true,title:'详情'},
name:'showMessage',
// 在path属性中的路径最后使用占位符声明接收params参数
// 格式 /:参数名/:参数名
// path:'showMessage/:id/:title',
// 注意:使用props配置的第三种方式时,如果接收的时query传递的参数,不能使用占位符声明接收参数
path:'showMessage',
component:ShowMessage,
// props配置的第一种写法:值为对象,这个对象中所有的key-value都会以props的形式传递给ShowMessage组件
// props:{
// a:1,
// b:'hello',
// }
// props配置的第二种写法:值为布尔值,如果布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传递给ShowMessage组件
// props:true,
// props配置的第三种写法:值为函数,这个函数返回的对象中每一组key-value都会通过props传给ShowMessage
props({query}){
return {
id:query.id,
title:query.title,
};
}
}
]
},
]
},
]
});
// 全局前置路由守卫-初始化的时候被调用,每次路由切换之前被调用
router.beforeEach((to,from,next)=>{
// meta用于存放一些路由的原数据,其中的属性名自定义即可,isAuth是否鉴权
if(to.meta.isAuth){//判断是否需要鉴权,为真则需要,为假则不需要
if(localStorage.getItem('school') === 'nkd1'){//劝降控制的具体规则
next();//放行
}else{
alert('无权限查看');//不符合规则就无法放行,所以提示无权限查看
}
}else{
next();//放行
}
});
// 全局后置路由守卫-初始化的时候被调用,每次路由切换之后被调用
router.afterEach((to,from)=>{
// console.log('后置路由守卫',to,from);
document.title = to.meta.title || '内科大';//修改网页的title
})
export default router;
独享守卫:
// 这个文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";
// 引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import ShowMessage from '../pages/ShowMessage'
// 创建并暴露一个路由器
const router = new VueRouter({
// 注意这里是routes
routes:[
// 每一对花括号就是一组路由规则
{
name:'guanyu',
path:'/about',
component:About,
meta:{isAuth:false,title:'关于'}
},
{
name:'zhuye',
path:'/home',
component:Home,
meta:{isAuth:false,title:'主页'},
children:[
{
name:'xinwen',
path:'news',
component:News,
meta:{isAuth:true,title:'新闻'},
// 独享守卫,只有前置守卫,没有后置守卫,规则和全局守卫一样,但只服务于当前守卫所在的路由规则中
beforeEnter:(to,from,next)=>{
if(to.meta.isAuth){//判断是否需要鉴权,为真则需要,为假则不需要
if(localStorage.getItem('school') === 'nkd1'){
next();
}else{
alert('无权限查看');
}
}else{
next();
}
}
},
{
name:'xiaoxi',
path:'message',
component:Message,
meta:{isAuth:true,title:'消息'},
children:[
{
meta:{isAuth:true,title:'详情'},
name:'showMessage',
// 在path属性中的路径最后使用占位符声明接收params参数
// 格式 /:参数名/:参数名
// path:'showMessage/:id/:title',
// 注意:使用props配置的第三种方式时,如果接收的时query传递的参数,不能使用占位符声明接收参数
path:'showMessage',
component:ShowMessage,
// props配置的第一种写法:值为对象,这个对象中所有的key-value都会以props的形式传递给ShowMessage组件
// props:{
// a:1,
// b:'hello',
// }
// props配置的第二种写法:值为布尔值,如果布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传递给ShowMessage组件
// props:true,
// props配置的第三种写法:值为函数,这个函数返回的对象中每一组key-value都会通过props传给ShowMessage
props({query}){
return {
id:query.id,
title:query.title,
};
}
}
]
},
]
},
]
});
// 全局后置路由守卫-初始化的时候被调用,每次路由切换之后被调用
router.afterEach((to,from)=>{
document.title = to.meta.title || '内科大';
})
export default router;
组件内守卫:
<template>
<div>
<h2>我是About的内容</h2>
</div>
</template>
<script>
export default {
name:'About',
// beforeDestroy(){
// console.log('About组件即将被销毁');
// },
// mounted(){
// console.log('About组件被挂载了');
// },
// 通过路由规则,进入到这个组件的时候被调用
// 注意是通过路由规则进入的,也就是通过路由规则使路径变化了才行,反之使用标签进入的不会触发组件内路由守卫的规则
beforeRouteEnter(to,from,next){
if(to.meta.isAuth){//判断是否需要鉴权,为真则需要,为假则不需要
if(localStorage.getItem('school') === 'nkd'){
next();
}else{
alert('学校名不对,无权限查看');
}
}else{
next();
}
},
// 通过路由规则,离开这个组件的时候被调用
beforeRouteLeave(to,from,next){
console.log('123');
next();
}
}
</script>
// 这个文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";
// 引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import ShowMessage from '../pages/ShowMessage'
// 创建并暴露一个路由器
const router = new VueRouter({
// 注意这里是routes
routes:[
// 每一对花括号就是一组路由规则
{
name:'guanyu',
path:'/about',
component:About,
meta:{isAuth:true,title:'关于'}
},
{
name:'zhuye',
path:'/home',
component:Home,
meta:{isAuth:false,title:'主页'},
children:[
{
name:'xinwen',
path:'news',
component:News,
meta:{isAuth:true,title:'新闻'},
// 独享守卫,只有前置守卫,没有后置守卫
// beforeEnter:(to,from,next)=>{
// if(to.meta.isAuth){//判断是否需要鉴权,为真则需要,为假则不需要
// if(localStorage.getItem('school') === 'nkd1'){
// next();
// }else{
// alert('无权限查看');
// }
// }else{
// next();
// }
// },
},
{
name:'xiaoxi',
path:'message',
component:Message,
meta:{isAuth:true,title:'消息'},
children:[
{
meta:{isAuth:true,title:'详情'},
name:'showMessage',
// 在path属性中的路径最后使用占位符声明接收params参数
// 格式 /:参数名/:参数名
// path:'showMessage/:id/:title',
// 注意:使用props配置的第三种方式时,如果接收的时query传递的参数,不能使用占位符声明接收参数
path:'showMessage',
component:ShowMessage,
// props配置的第一种写法:值为对象,这个对象中所有的key-value都会以props的形式传递给ShowMessage组件
// props:{
// a:1,
// b:'hello',
// }
// props配置的第二种写法:值为布尔值,如果布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传递给ShowMessage组件
// props:true,
// props配置的第三种写法:值为函数,这个函数返回的对象中每一组key-value都会通过props传给ShowMessage
props({query}){
return {
id:query.id,
title:query.title,
};
}
}
]
},
]
},
]
});
// 全局前置路由守卫-初始化的时候被调用,每次路由切换之前被调用
// router.beforeEach((to,from,next)=>{
// if(to.meta.isAuth){//判断是否需要鉴权,为真则需要,为假则不需要
// if(localStorage.getItem('school') === 'nkd1'){
// next();
// }else{
// alert('无权限查看');
// }
// }else{
// next();
// }
// });
// 全局后置路由守卫-初始化的时候被调用,每次路由切换之后被调用
router.afterEach((to,from)=>{
// console.log('后置路由守卫',to,from);
document.title = to.meta.title || '内科大';
})
export default router;
13、路由的两种工作模式
对于一个url来说,#及其和后面的内容就是hash值,hash值不会包含在HTTP请求中,也就是hash不会带给服务器
hash模式:地址中永远带着#号,不美观,如果以后把地址通过第三方手机app分享,要是app校验严格的话,则地址会被标记为不合法,兼容性较好
history模式:地址干净,美观兼容性和hash相比略差,应用部署上线时需要后端人员支持,解决刷新页面后服务器端404的问题