Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
说在前面
- vue单页应用通过路由(vue-router)实现页面跳转;所以单页应用才需要路由
- 一个页面跳转是一个路由(route),他是一组映射关系(key - value)
- key是路径,value是组件
- 多个路由需要路由器(router)进行管理
- vue-router在vue中使用时是在vue实例添加一个配置项叫router
- router和vue实例一样需要new,所以VueRouter的首字母大写,new时需要传入一个对象作为配置项,对象含一个重要属性routers,属性值为数组;也可以传入其他属性如mode
- vue-router是vue的一个插件,使用三步曲
- 安装npm i vue-router
- 引入import VueRouter from 'vue-router'
- 使用Vue.use(VueRouter)
路由中几个重要的点
- 路由的切换
- 可以借助 router-link 标签实现
- 也可以使用 push、 replace、 forward、 back、 go 等js方法实现
- 需要指定组件呈现的位置 使用
<router-view></router-view> - 路由组件通常放到pages文件夹下面,一般组件放到components文件夹
- 通过切换,隐藏了的路由组件,默认是被销毁了的,需要的时候会再挂载
- 每个组件都有自己的$route属性,里面存储着自己的路由信息
- 整个应用只有一个router,可以通过组件的$router属性获取到
实现一个简单的路由切换
- 路由中的组件可以先引入后使用,也可以按需引入
- 引入路由时文件的.vue后缀可以省略;引入js文件时,如果引入某个文件夹下的index.js,那么index.js可以省略;建议不要省略
- 配置路由的时候path对应的属性值必须加上'/'
公用代码如下:
main.js
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入VueRouter
import VueRouter from 'vue-router'
//引入路由器
import router from './router'
//关闭Vue的生产提示
Vue.config.productionTip = false
//应用插件
Vue.use(VueRouter)
//创建vm
new Vue({
el:'#app',
render: h => h(App),
router:router
})
router/index.js
// 引入路由
import VueRouter from 'vue-router'
//引入组件(先定义后使用)
import Home from '../pages/home.vue'
//创建并暴露一个路由器
export default new VueRouter({
routes:[
{
path:'/about',
// 也可以按需引入
component: () => import('@/pages/about.vue')
},
{
path:'/home',
component:Home
}
]
})
pages/home.vue
<template>
<h2>我是home的内容</h2>
</template>
<script>
export default {
name:'Home'
}
</script>
pages/about.vue
<template>
<h2>我是about的内容</h2>
</template>
<script>
export default {
name:'About'
}
</script>
使用router-link标签实现
- 使用的时候to的值可以不加前面的'/',为了兼容性和统一性建议加上
- router-link标签
- 默认被解析为a标签;通过tag属性修改
- 默认激活(非精准匹配)时添加类名router-link-active;通过active-class属性修改
- 默认激活(精准匹配)时添加类名router-link-exact-active;通过exact-active-class属性修改
- 精准匹配是值to的值完全和路由相等,非精准匹配是指路由被包含就会触发;特别注意to='/'在非精准匹配的情况下会一直被触发active;如果要完全按照精准匹配来可以设置
<router-link
to="/about"
active-class="active">About</router-link><br>
<router-link to="/home">Home</router-link><br>
<router-link to="/about/list"
exact-active-class="active1">/about/list</router-link><br>
<!-- 用于展示路由组件 -->
<router-view></router-view>
使用push方法标签实现
<template>
<div id="app">
<hello msg="Hello"/>
<li @click="goToRouter('/about')">About</li>
<li @click="goToRouter('/home')">Home</li>
<!-- 用于展示路由组件 -->
<router-view></router-view>
</div>
</template>
<script>
import hello from './components/hello.vue'
export default {
name: 'App',
components: {
hello
},
methods: {
goToRouter(path){
this.$router.push({path});
}
}
}
</script>
多级路由
- 为需要子路由的路由对象添加
children属性,属性值是一个数组可以放多个子路由,每个子路由的配置项和父级路由完全一样 - 特别注意,是哪个组件(页面)的子路由,那么
<router-view></router-view>就应该写在那个组件(页面)里面 - 子路由的path属性值如果前面加/,跳转时路径应该配置为path属性对应的完整值;如果子路由的path属性前面不加/,跳转时路径配置为父级路由的path属性值+/+子路由的path属性值
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import Home from '../pages/home'
import About from '../pages/about'
//创建并暴露一个路由器
export default new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[{
path:'/about/list',
component:() => import('../pages/list.vue')
}]
}
]
})
命名路由和动态路由
命名路由:
- 在配置路由的时候添加一个name属性;
- 可以直接使用name属性跳转,不必使用path
- 使用命名路由,使用params参数时可以自动解码和编码
动态路由:
- 使用:的格式占位,后面跟着的名称对应params中的属性名
- 使用path跳转,将属性值拼接在路径后面即可,页面跳转后刷新页面params不会消失,在跳转之后的页面中使用$route.params接收参数;多拼接参数将不能跳转(路径不对)
- 使用name跳转时,只需配置params对象属性,对象属性中可以添加任意的参数,如果参数名和动态路由中的属性名一致,那么页面刷新params参数还在,否则页面刷新params参数消失
router/index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import Home from '../pages/home'
import About from '../pages/about'
//创建并暴露一个路由器
export default new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[{
// 这里就是动态路由
path:'/list/:id/:title',
name:'list',
component:() => import('../pages/list.vue')
}]
}
]
})
home.vue
<template>
<div>
<h2>我是home的内容</h2>
<button @click="goTo">跳转</button>
<!-- <router-link to="/list/123/456">跳转</router-link> -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'Home',
methods: {
goTo(){
this.$router.push({
name:'list',
params:{
id:'123',
title:'456',
nr:'789'
}
})
}
}
}
</script>
list/index.js
<template>
<h2>我是list的内容{{$route.params}}</h2>
</template>
<script>
export default {
name:'List'
}
</script>
路由参数
params参数
- 使用了占位符(动态路由)的params参数,会跟在路径后面,刷新不会消失
- 其实不只是命名路由可以传递params参数,只是使用path传递params参数需要提前定义成动态路由,留出params参数的位置
- 更多内容看上面一节
- 获取参数使用$route.params
query参数
- 类似http的get请求的参数,拼接到请求路径后面
- 使用方法:可以直接拼接在path路径上;也可以使用query属性
- 以下几种方法都是可以的
<template>
<div>
<h2>我是about的内容</h2>
<router-link to="/news?a=123&b=456">
点击跳转news
</router-link>
<br>
<router-link :to="{path:'/news',query:{a:1}}">
点击跳转news
</router-link>
<br>
<router-link :to="{name:'news',query:{a:1,b:1}}">
点击跳转news
</router-link>
<br>
<button @click="goTo">点击跳转news</button>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'About',
methods: {
goTo(){
this.$router.push({
name:'news',
query:{
id:'123',
title:'456',
nr:'789'
}
})
}
}
}
</script>
所以想要刷新参数不消失,要么直接传query参数,要么传params参数的时候,在路由中使用动态路由占位(命名路由使用params传参数是不需要占位的,但是不占位刷新页面参数会丢失)
路由跳转的其他方式
- 除了push还可以使用replace
- 不同的是push是向浏览器历史记录中添加一条记录;replace是替换浏览器中的当前记录
- router-link默认开启的是push跳转方式;添加replace改变跳转方式
<router-link replace to="/news">news</router-link>
- 作用是使用replace跳转的路由,点击浏览器的返回按钮,页面将跳转到之前不是使用replace跳转的页面
- $router.forward()前进
- 前进只针对于有浏览器历史记录的路由
- $router.back()后退,同前进
- 如果上一个路由使用replace方式跳转,则后退不到上一个路由
- $router.go()可前进也可后退
props配置
- 可以在路由的配置项中添加props配置项;在路由组件中以props的形式接收
- 配置方式有三种
- 直接是个对象,对象里面的属性将在跳转的路由组件中以props的形式被接收
- 可以是布尔值,如果为true,则把路由收到的所有params参数可以在路由组件中以props的形式接收
- 上面那种形式props只能接收params传的值;可以函数的形式传递route中的任意参数,如query参数
keep-alive组件标签
- 是 Vue 内置的一个组件,可以使被包含的组件保留状态,让不展示的组件保持挂载,不被销毁
- 可以缓存任意组件(包含一般组件和路由组件)
// 缓存一般组件
<keep-alive>
<hello @click="change(arguments,2)" msg="Hello"/>
</keep-alive>
// 缓存路由组件
<keep-alive exclude="About">
<router-view></router-view>
</keep-alive>
- 可以在标签添加两个属性include和exclude,值可以是字符串或者正则表达式
- include-只有匹配组件名的组件会被缓存
// 只有About路由组件可以被缓存
<keep-alive include="About">
<router-view></router-view>
</keep-alive>
- exclude-任何匹配组件名的组件都不会被缓存
// 只有About路由组件不可以被缓存
<keep-alive exclude="About">
<router-view></router-view>
</keep-alive>
- 被缓存的组件新增两个生命周期钩子
- activated();组件每次被激活时触发
- deactivated();离开被缓存的组件时激活
上图可以看到:
- 只有第一次进入缓存组件时才会触发
created和mounted相关钩子函数,后面进入缓存组件不会触发 - 离开不会触发destroyed相关钩子
- 每次进入会触发activated钩子
- 每次离开会触发deactivated钩子
- 因为组件没有销毁所以离开再次进入都会触发updated相关钩子
- 只想缓存某些路由组件;除了include和exclude,还可以配置路由实现
- 配置路由的meta属性;meta称为路由元信息,你可以将任意信息附加到路由上;如权限点等,这些信息都可以配置到meta对象属性中
- 下面例子中message路由组件将不会被缓存,news路由组件将被缓存
// router/index.js
export default new VueRouter({
routes:[
{
path:'/about',
name:'about',
component:About,
children:[{
path:'/news',
name:'news',
component:() => import('../pages/news.vue'),
meta: {
keepAlive:true
}
},{
path:'/message',
name:'message',
component:() => import('../pages/message.vue'),
meta: {
keepAlive:false
}
}]
}
]
})
// about组件
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
路由守卫
- 作用:对路由进行权限控制;通过取消和跳转的方式来进行导航守卫
- 分类:全局守卫、独享守卫、组件内守卫
- 全局守卫又分为全局前置守卫和全局后置守卫
- 全局前置守卫(beforeEach): 初始化时执行一次,每次路由切换前执行;守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中
该守卫方法接收三个参数
to:即将进入的目标导航,包含该路由的全部信息
from:当前导航正要离开的路由;信息包含的内容项和to一样;如果是初始化进入时from的信息和to的信息完全一致 next:调用next方法,将跳转到管道中的下一个路由;也可以指定跳转路由
router.beforeEach((to,from,next)=>{
if(!to.meta.isLogin && to.path !== '/home'){
// 指定跳转路由
next({path:'/home'});
}else {
next()
}
})
- 全局后置守卫
- 初始化的时候被调用一次,每次路由切换的时候被调用
- 参数只有to和form,一般用于修改网页的title等
router.afterEach((to,from)=>{
console.log('后置路由守卫',to,from)
document.title = to.meta.title || '系统'
})
- 独享守卫
- 写在需要守卫的路由配置中
- 如果只针对某个路由进行守卫,可以使用独享守卫
- 每次进入当前路由的时候执行,参数和全局前置守卫一样,能做的操作也是一样
- 如果既有全局前置守卫也有独享守卫,先执行全局前置守卫再执行独享守卫
beforeEnter(to,from,next){
console.log('独享路由守卫',to,from);
next()
}
- 组件内守卫:分为进入守卫(beforeRouteEnter)和离开守卫(beforeRouteLeave) 写在组件内部,和生命周期钩子一样属于组件配置项的一个属性
进入守卫:
- 通过路由规则进入该组件时被调用;在进入组件的beforeCreated之前会被调用,所以在此时还没有this
- 在此时可以修改路由参数中的任意值;下面就是根据query中的参数修改meta的值
// 通过路由规则,进入该组件时被调用
beforeRouteEnter(to, from, next) {
if (to.query.jdmc) {
const last = `${to.query.jdmc}阶段风险问题统计`;
to.meta.breadcrumb = ['风险防控', '执行风险防控', '风险概览'];
to.meta.breadcrumb.push(last);
}
next();
}
离开守卫;离开组件时被调用
beforeRouteLeave (to, from, next) {
console.log('About--beforeRouteLeave',to,from)
next()
}
路由工作的两种模式
- hash模式:
-
- 对于一个url来说,什么是hash值?
-
- #及其后面的内容就是hash值。
- hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
- 所以部署上线的时候不用返回每个地址,所以不需要特殊处理
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性较好。
- history模式:
-
- 地址干净,美观 。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
-
- 因为地址改变就会请求服务器,所以需要服务器能返回对应的地址