1 简介
vue-router是vue的一个插件,专门用来实现SPA应用。SPA也就是单页Web应用,特点是:整个应用只有一个完整的页面,点击页面中的导航链接不会刷新页面,只会做页面的局部更新,数据需要通过ajax请求获取
2 路由的基本使用
先来个简单的示例,点击About和Home路由到不同的额组件,展示组件内容
2.1 安装
注意vue2对应router3,vue3对应router4
npm i vue-router@3 #安装的router3
2.2 配置路由器
在src下创建一个router文件夹,再创建一个index.js
//引入router
import VueRouter from 'vue-router'
// 引入组件
import AboutComp from '../pages/AboutComp'
import HomeComp from '../pages/HomeComp'
// 创建并暴露一个路由器-路由器里面配置路由
export default new VueRouter({
routes:[
{
path:'/about', //路径,可随便定义-但是要和<router-link>标签里面的to属性对应上
component:AboutComp //组件名
},
{
path:'/home',
component:HomeComp
}
]
})
2.3 main.js配置router到vm
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router' // 引入VueRouter
import router from './router' // 引入路由器
Vue.config.productionTip = false
Vue.use(VueRouter) // 应用Router插件
new Vue({
el:'#app',
render: h => h(App),
router:router //配置router到vm上
})
2.4 组件使用路由
1)app.vue
使用路由,主要涉及到两个标签<router-link>和<router-view>
1、router-link标签就相当于导航(最后会被解析成一个a标签)
active-class="active":点击的时候高亮
to:点击的时候路由到哪个组件,这里的值和配置路由器里面的路由的path的值要对应
<router-link class="list-group-item"
active-class="active" to="/home">Home</router-link>
2、<router-view>
路由到的组件要显示在哪里,就在哪里写这个标签
<template>
<div>
<div class="row">
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header"><h2>Vue Router Demo</h2></div>
</div>
</div>
<div class="row">
<div class="col-xs-2 col-xs-offset-2">
<div class="list-group">
<!-- Vue中借助router-link标签实现路由的切换 -->
<router-link class="list-group-item"
active-class="active" to="/about">About</router-link>
<hr/>
<router-link class="list-group-item"
active-class="active" to="/home">Home</router-link>
</div>
</div>
<div class="col-xs-6">
<div class="panel">
<div class="panel-body">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name:'App'
}
</script>
2)AboutComp.vue
<template>
<h2>我是About的内容</h2>
</template>
<script>
export default {
name:'AboutComp'
}
</script>
3)HomeComp.vue
<template>
<h2>我是Home的内容</h2>
</template>
<script>
export default {
name:'HomeComp'
}
</script>
4)注意事项
注意事项:一般我们把路由组件放在src/pages,其它组件放在src/components下面。
2.5 效果
点击About
点击Home
3 路由切换时组件的情况
默认情况下,从一个组件切换到另一个组件,原组件是会被销毁的
当然,也可以通过配置使它不被销毁而是缓存起来(后面会说到)
在AboutComp里面加个destoryed函数
<template>
<h2>我是About的内容</h2>
</template>
<script>
export default {
name:'AboutComp',
destroyed() {
console.log('AboutComp正要被销毁')
},
}
</script>
当点击了About,再点击Home时,About的destroyed函数被调用,说明它被销毁了
4 $router和$route
$router是路由器对象,只有一个。$route是路由对象,每个组件都有自己的route对象。
它们里面有很多我们需要的数据。
在app.vue和AboutComp.vue和HomeComp.vue都加上一个mounted函数,打印$router和$route。
1)app.vue
<script>
export default {
name:'App',
mounted() {
console.log('app@' , this.$router)
console.log('app@' , this.$route)
},
}
</script>
2)AboutComp.vue
<script>
export default {
name:'AboutComp',
mounted() {
console.log('AboutComp@' , this.$router)
console.log('AboutComp@' , this.$route)
},
}
</script>
3) HomeComp.vue
<script>
export default {
name:'HomeComp',
mounted() {
console.log('HomeComp@' , this.$router)
console.log('HomeComp@' , this.$route)
},
}
</script>
5 多级路由
如在AboutComp组件加一个子路由NewsComp
5.1 配置;路由器index.js
在about路由里面配置一个属性childrens,它是一个数组,里面又可以写多个路由配置对象(注意childrens里面的对象的path开头不要/)
//引入router
import VueRouter from 'vue-router'
// 引入组件
import AboutComp from '../pages/AboutComp'
import HomeComp from '../pages/HomeComp'
import NewsComp from '../pages/NewsComp'
// 创建并暴露一个路由器-路由器里面配置路由
export default new VueRouter({
routes:[
{
path:'/about', //路径,可随便定义-但是要和<router-link>标签里面的to属性对应上
component:AboutComp, //组件名
children:[
{
path:'news', //注意,这里不要写/
component:NewsComp
}
]
},
{
path:'/home',
component:HomeComp
}
]
})
5.2 AboutComp.vue
加上router-link和router-view标签
注意:router-link标签里面的to要写完整路径(把父组件的路径也带上)
<template>
<div>
<h2>我是About的内容</h2>
<div>
<router-link class="list-group-item"
active-class="active" to="/about/news">News</router-link>
</div>
<div class="panel-body">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name:'AboutComp',
mounted() {
console.log('AboutComp@' , this.$router)
console.log('AboutComp@' , this.$route)
},
}
</script>
5.3 NewsComp.vue
<template>
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
</template>
<script>
export default {
name:'NewsComp'
}
</script>
5.4 效果
点击About
点击News
6 命名路由
6.1 简介
上面我们在src/router/index.js配置路由的时候,采用的是path属性。在router-link的to属性采用/path1/path2..来路有跳转。
我们还可以使用name属性来给路由命名,这样子在to属性只需要写成{name:'name的值'}
路由的命名要注意不要重复
6.2 示例
在上面的示例中,我们采用的是path属性,访问News组件需要写成/path1/path2的格式
routes:[
{
path:'/about', //路径,可随便定义-但是要和<router-link>标签里面的to属性对应上
component:AboutComp, //组件名
children:[
{
path:'news', //注意,这里不要写/
component:NewsComp
}
]
},
{
path:'/home',
component:HomeComp
}
]
<router-link class="list-group-item"
active-class="active" to="/about/news">News</router-link>
下面,为News组件的路由命名为newsName
1)index.js
//引入router
import VueRouter from 'vue-router'
// 引入组件
import AboutComp from '../pages/AboutComp'
import HomeComp from '../pages/HomeComp'
import NewsComp from '../pages/NewsComp'
// 创建并暴露一个路由器-路由器里面配置路由
export default new VueRouter({
routes:[
{
path:'/about', //路径,可随便定义-但是要和<router-link>标签里面的to属性对应上
component:AboutComp, //组件名
children:[
{
name:'newsName', //给路由命名
path:'news', //注意,这里不要写/
component:NewsComp
}
]
},
{
path:'/home',
component:HomeComp
}
]
})
2)AboutComp.vue
注意router-link的to属性写成对象的形式,所以to前面要加上:是的to里面的额内容解析为js代码而不是字符串
<template>
<div>
<h2>我是About的内容</h2>
<div>
<router-link class="list-group-item"
active-class="active" :to="{
name:'newsName'
}"
>News</router-link>
</div>
<div class="panel-body">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name:'AboutComp',
mounted() {
console.log('AboutComp@' , this.$router)
console.log('AboutComp@' , this.$route)
},
}
</script>
3)效果
7 路由传参数
路由可以通过query和params两种方式传递参数
7.1 query传递参数
7.1.1 传递参数
只需要在router-link的to属性的对象里面加上一个query对象,里面key:value传值
:to="{
name:'newsName',
query:{
id:'query传递的第一个参数',
name:'query传递的第二个参数'
}
}"
7.1.2 接收参数
在结束传输的组件通过$route.query获取
$route.query.属性名
$route.query.id
$route.query.name
7.1.3 示例
在上面的基础上,对AboutComp.vue和NewsComp.vue进行修改,从AboutComp.vue传递参数到NewsComp.vue
1)AboutComp.vue
<template>
<div>
<h2>我是About的内容</h2>
<div>
<router-link class="list-group-item"
active-class="active" :to="{
name:'newsName',
query:{
'id':'query传递的第一个参数',
'name':'query传递的第二个参数'
}
}"
>News</router-link>
</div>
<div class="panel-body">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name:'AboutComp',
mounted() {
console.log('AboutComp@' , this.$router)
console.log('AboutComp@' , this.$route)
},
}
</script>
2)NewsComp.vue
<template>
<ul>
<li>{{$route.query.id}}</li>
<li>{{$route.query.name}}</li>
<li>news003</li>
</ul>
</template>
<script>
export default {
name:'NewsComp'
}
</script>
3)效果
7.2 params传递参数
注意:params传递参数只能使用name写法不能使用path写法
7.2.1 传递参数
只需要在router-link的to属性的对象里面加上一个params对象,里面key:value传值
:to="{
name:'newsName',
params:{
'id':'query传递的第一个参数',
'name':'query传递的第二个参数'
}
}"
7.2.2 声明接收的参数
在结束的组件的路由配置的path属性声明接收的参数,在path属性的值后面添加/:参数名,可添加多个
path:'news/:参数1名称/:参数2名称', //:xx 用于声明接收param参数
7.2.3 接收参数
在结束传输的组件通过$route.params获取
$route.params.属性名
$route.params.id
$route.params.name
7.1.3 示例
在上面的基础上,对index.js和AboutComp.vue和NewsComp.vue进行修改,从AboutComp.vue传递参数到NewsComp.vue
1)index.js
//引入router
import VueRouter from 'vue-router'
// 引入组件
import AboutComp from '../pages/AboutComp'
import HomeComp from '../pages/HomeComp'
import NewsComp from '../pages/NewsComp'
// 创建并暴露一个路由器-路由器里面配置路由
export default new VueRouter({
routes:[
{
path:'/about', //路径,可随便定义-但是要和<router-link>标签里面的to属性对应上
component:AboutComp, //组件名
children:[
{
name:'newsName', //给路由命名
path:'news/:id/:name', //:xx 用于声明接收param参数
component:NewsComp
}
]
},
{
path:'/home',
component:HomeComp
}
]
})
2)AboutComp.vue
<template>
<div>
<h2>我是About的内容</h2>
<div>
<router-link class="list-group-item"
active-class="active" :to="{
name:'newsName',
params:{
'id':'params传递的第一个参数',
'name':'params传递的第二个参数'
}
}"
>News</router-link>
</div>
<div class="panel-body">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name:'AboutComp',
mounted() {
console.log('AboutComp@' , this.$router)
console.log('AboutComp@' , this.$route)
},
}
</script>
3)NewsComp.vue
<template>
<ul>
<li>{{$route.params.id}}</li>
<li>{{$route.params.name}}</li>
<li>news003</li>
</ul>
</template>
<script>
export default {
name:'NewsComp'
}
</script>
4)效果
8 路由的props配置
它的作用是参数名称映射
8.1 示例
1)index.js
在news组件的路由配置加上了props属性
//引入router
import VueRouter from 'vue-router'
// 引入组件
import AboutComp from '../pages/AboutComp'
import HomeComp from '../pages/HomeComp'
import NewsComp from '../pages/NewsComp'
// 创建并暴露一个路由器-路由器里面配置路由
export default new VueRouter({
routes:[
{
path:'/about', //路径,可随便定义-但是要和<router-link>标签里面的to属性对应上
component:AboutComp, //组件名
children:[
{
name:'newsName', //给路由命名
path:'news/:id/:name', //:xx 用于声明接收param参数
component:NewsComp,
props($route){ //props属性配置,用于参数映射
return {
id:$route.params.id,
name:$route.params.name
}
}
}
]
},
{
path:'/home',
component:HomeComp
}
]
})
2)NewsComp.vue
在NewsComp.vue的props属性中添加在index.js中的props属性映射
这样子就可以通过{{id}} {{name}} 直接访问到传递过来的参数
<template>
<ul>
<li>{{id}}</li>
<li>{{name}}</li>
<li>news003</li>
</ul>
</template>
<script>
export default {
name:'NewsComp',
props:['id','name']
}
</script>
对比下
使用props映射前
<li>{{$route.query.id}}</li>
<li>{{$route.query.name}}</li>
使用props映射后
<li>{{id}}</li>
<li>{{name}}</li>
感觉这个属性不是很好用,vc上的属性来源已经很多了,名字容器重复,还不如直接$route去获取
9 replace属性
9.1 浏览器的历史记录简介
当我们点击导航一层层往下路由组件时,可以通过浏览器的后退键回退,回退后,又可以通过前进键前进
浏览器记录历史分为两种,push和replace,默认情况下就是push
9.2 push
push是压栈的模式,每次路由都把新的放到最上面
9.3 replace
replace是用新的替换旧的
9.4 路由的replace属性
1)示例
只需要在<router-link>标签加上属性replace即可开启这个组件的replace模式
在AboutComp的<router-link>标签加上replace属性
<router-link class="list-group-item"
active-class="active" replace :to="{
name:'newsName',
params:{
'id':'params传递的第一个参数',
'name':'params传递的第二个参数'
}
}"
>News</router-link>
那么about跳转到news的时候,news的记录会替换about的记录
2)效果
点击回退,直接回到了最初而不是about组件的内容
10 编程式路由
上面都是采用<router-link>标签来完成的导航,除了它,还可以使用编程式。借助$router来实现
10.1 $router的几个函数介绍
$router.push({}) //代替<router-link>,push模式的,里面传入的对象和router-link的to里面的额对象一样
$router.replace({}) //代替<router-link>,push模式的,里面传入的对象和router-link的to里面的额对象一样
$router.forward() //前进-等同于浏览器的前进键
$router.back() //后退-等同于浏览器的后推键
$router.to(n) //前进或后退 n为数组,正数标识前进几个,负数标识后退几个
10.2 示例
修改AboutComp.vue 使用$router代替<router-link>标签
<template>
<div>
<h2>我是About的内容</h2>
<div>
<button @click="cli">news </button>
</div>
<div class="panel-body">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name:'AboutComp',
methods: {
cli(){
this.$router.push({
name:'newsName',
params:{
'id':'params传递的第一个参数',
'name':'params传递的第二个参数'
}
})
}
},
}
</script>
11 缓存路由组件
在最开始讲到,有一个组件路由到另一个组件,原组件默认会被销毁,我们可以设置它不被销毁,而是缓存起来
11.1 格式
想要某些组件不被销毁,在这些组件存放的位置<router-view>标签外层加上<keep-alive>标签。那么,这里出现的这些组件会不被销毁
1)某个组件不被销毁
// 缓存一个路由组件
<keep-alive include="News"> // include中写想要缓存的组件名,不写表示全部缓存
<router-view></router-view>
</keep-alive>
2)多个组件不被销毁
// 缓存多个路由组件
<keep-alive :include="['News','Message']">
<router-view></router-view>
</keep-alive>
3)全部组件不被销毁
// 缓存多个路由组件
<keep-alive >
<router-view></router-view>
</keep-alive>
11.2 示例
1)AboutComp.vue
加上<keep-alive>标签
<template>
<div>
<h2>我是About的内容</h2>
<div>
<button @click="cli">news </button>
</div>
<keep-alive include="NewsComp">
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
name:'AboutComp',
methods: {
cli(){
this.$router.push({
name:'newsName',
params:{
'id':'params传递的第一个参数',
'name':'params传递的第二个参数'
}
})
}
},
}
</script>
2)NewsComp.vue
加上destroyed函数
<template>
<ul>
<li>{{id}}</li>
<li>{{name}}</li>
<li>news003</li>
</ul>
</template>
<script>
export default {
name:'NewsComp',
props:['id','name'],
destroyed() {
console.log('news组件销毁')
},
}
</script>
3)效果
点击回退,newscomp的destroyed没有被触发,说明没有销毁
12 路由独有的两个生命函数
12.1 简介
路由独有两个生命函数,分别为activited和deactivited
activited:被 keep-alive 缓存的组件失活时调用
deactivited:被 keep-alive 缓存的组件激活时调用
官方文档:https://v2.cn.vuejs.org/v2/api/
12.2 注意事项
1)activated和deactivated是配合keep-alive一起使用的
2)activated和deactivated没有keep-alive的时候是不会被触发的
3)当keepalive页面缓存,有activated钩子和created钩子函数时,这两个函数会被同时触发,此时可以使用activated代替created,因为created只会触发一次
4)页面被缓存下来的时候,不会触发destroyed生命钩子,取而代之触发的是deactivated钩子
12.3 它的作用
keepalive页面缓存下来的时候,只会调用一次created这个钩子函数,因为已经被缓存下来了,所以我们重新进入这个组件的时候, 就不会触发created钩子了,created这个钩子就是只有页面渲染的时候会触发。那么就可以使用的activated钩子函数。deactivated同样如此,从一个离开的时候,由于缓存destoryed不会触发,那么就可以使用的deactivated钩子函数
12.4 方法格式
<script>
export default {
name:'News',
data(){
return{
opacity:1
}
},
activated(){
console.log('News组件被激活了')
},
deactivated(){
console.log('News组件失活了')
}
}
</script>
13 路由守卫
13.1 简介
它的作用是对路由器进行权限控制,可以理解为拦截器类似的东西
它分为三类:全局守卫、独享守卫、组件内守卫
13.2 全局路由守卫
初始化的时候、每次路由切换之前被调用,针对所有路由组件
13.2.1 守卫函数
beforeEach :前置守卫,初始化时、每次路由切换前执行
affterEach :后置守卫,初始化时、每次路由切换后执行
13.2.2 配置格式
在router/index.js配置
//引入router
import VueRouter from 'vue-router'
// 引入组件
import AboutComp from '../pages/AboutComp'
import HomeComp from '../pages/HomeComp'
import NewsComp from '../pages/NewsComp'
// 创建并暴露一个路由器-路由器里面配置路由
const router = new VueRouter({
routes:[
{
path:'/about', //路径,可随便定义-但是要和<router-link>标签里面的to属性对应上
component:AboutComp, //组件名
children:[
{
name:'newsName', //给路由命名
path:'news/:id/:name', //:xx 用于声明接收param参数
component:NewsComp,
props($route){
return {
id:$route.params.id,
name:$route.params.name
}
}
}
]
},
{
path:'/home',
component:HomeComp
}
]
})
// 全局前置路由守卫————初始化的时候、每次路由切换之前被调用
router.beforeEach((to,from,next) => { //to是要去的组件,from是原组件,next是个函数,调用后才会切换到to的组件,否则不切换
console.log('前置路由守卫',to,from)
//逻辑处理
next() //放行
})
// 全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to,from)=>{
console.log('后置路由守卫',to,from)
//逻辑处理
})
// 导出路由器
export default router
13.2.3 meta.isAuth
在使用全局路由守卫的时候,如果只是部分路由组件需要守卫,那么就可以此使用meta属性
在meta中配置属性isAuth:true,标识这个路由组件需要守卫,这样子,我们在全局守卫的函数就可以通过meta.isAuth去判断哪些需要守卫了
{
name:'newsName', //给路由命名
path:'news/:id/:name', //:xx 用于声明接收param参数
component:NewsComp,
meta:{
isAuth:true
}
}
守卫函数,通过meta.isAuth判断
router.beforeEach((to,from,next) => {
if(to.isAuth){ //判断是否需要守卫
console.log('前置路由守卫',to,from)
//逻辑处理
}
next() //放行
})
完整
//引入router
import VueRouter from 'vue-router'
// 引入组件
import AboutComp from '../pages/AboutComp'
import HomeComp from '../pages/HomeComp'
import NewsComp from '../pages/NewsComp'
// 创建并暴露一个路由器-路由器里面配置路由
const router = new VueRouter({
routes:[
{
path:'/about', //路径,可随便定义-但是要和<router-link>标签里面的to属性对应上
component:AboutComp, //组件名
children:[
{
name:'newsName', //给路由命名
path:'news/:id/:name', //:xx 用于声明接收param参数
component:NewsComp,
props($route){
return {
id:$route.params.id,
name:$route.params.name
}
},
meta:{
isAuth:true
}
}
]
},
{
path:'/home',
component:HomeComp
}
]
})
// 全局前置路由守卫————初始化的时候、每次路由切换之前被调用
router.beforeEach((to,from,next) => { //to是要去的组件,from是原组件,next是个函数,调用后才会切换到to的组件,否则不切换
if(to.isAuth){
console.log('前置路由守卫',to,from)
//逻辑处理
}
next() //放行
})
// 全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to,from)=>{
console.log('后置路由守卫',to,from)
//逻辑处理
})
// 导出路由器
export default router
13.3 独享路由守卫
指定路由切换之后被调用
13.3.1 守卫函数
beforeEnter :切换路由前触发
13.3.2 配置格式
在NewsComp这个组件里面配置独享路由守卫,那么就只会对它生效
//引入router
import VueRouter from 'vue-router'
// 引入组件
import AboutComp from '../pages/AboutComp'
import HomeComp from '../pages/HomeComp'
import NewsComp from '../pages/NewsComp'
// 创建并暴露一个路由器-路由器里面配置路由
const router = new VueRouter({
routes:[
{
path:'/about', //路径,可随便定义-但是要和<router-link>标签里面的to属性对应上
component:AboutComp, //组件名
children:[
{
name:'newsName', //给路由命名
path:'news/:id/:name', //:xx 用于声明接收param参数
component:NewsComp,
// 🔴独享守卫,特定路由切换之后被调用
beforeEnter(to,from,next){
console.log('独享路由守卫',to,from)
//逻辑处理
next()
},
props($route){
return {
id:$route.params.id,
name:$route.params.name,
}
},
}
]
},
{
path:'/home',
component:HomeComp
}
]
})
// 导出路由器
export default router
13.4 组件内路由守卫
13.4.1 守卫函数
beforeRouterEnter :通过路由进入该组件时被调用(非路由进入组件的不会调研)
beforeRouterLeave :通过路由离开该组件时被调用(非路由进入组件的不会调研)
13.4.2 配置格式
和上面两个不同,它是在组件文件里面配置的
<template>
<div>
<h2>我是About的内容</h2>
<div>
<button @click="cli">news </button>
</div>
<keep-alive include="NewsComp">
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
name:'AboutComp',
methods: {
cli(){
this.$router.push({
name:'newsName',
params:{
'id':'params传递的第一个参数',
'name':'params传递的第二个参数'
}
})
}
},
beforeRouteEnter (to, from, next) {
console.log('About--beforeRouteEnter',to,from)
next()
},
// 通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
console.log('About--beforeRouteLeave',to,from)
next()
}
}
</script>