上篇文章,我讲了路由外面的,动静路由匹配,编程式导航,嵌套路由匹配,命名路由,命名视图,重定向这几个知识点,然而官网vue-router上,不仅仅是这些内容,我提到的这些知识点是针对于实在的我的项目开发中用到进行解说,如果想理解更多vue-router的常识能够查阅官网内容

回顾一下上节的内容,咱们在讲动静路由匹配和编程式导航的时候,组件中是如何获取由路由传递过去的值呢?

动静路由匹配:

<template> <section> {{$route.params.taskId}} </section></template>

编程式导航:

<template> <section> {{$route.query.taskId}} </section></template>

有没有发现,在具体组件里,咱们应用的是$route.params$route.query来获取路由传递过去的参数,如果这个组件用到其它路由上面呢,当路由不不携带参数时,组件里有这么个玩意儿$route.query.taskId就会报错,所以,采纳上述两种形式,当须要组件复用时,因为路由和组件之间有着高度的耦合,不能最大水平复用组件,那么就须要采纳其它形式了,那就是路由组件传参

  1. 路由组件传参

路由组件传参也有3中形式实现:

第1种:布尔模式

批改src/router/index.js外面的路由:

 { path: '/task-detail/:taskId', name: 'task-detail', component: () => import('../views/task-detail.vue'), props: true },

组件外面获取路由传递过去的值:

<template> <section> {{taskId}} </section></template><script>export default { props: { taskId: { type: [String, Number], default: '' } }, data () { return { } }}</script>

这样,当须要这个组件复用时,要想给组件传递taskId这个值,只须要调用这个组件即可

<task-detail :taskId="10000218"></task-detail>

通过路由给组件传值的时候要这样写:

<router-link to="/task-detail/10000217">工作详情10000217</router-link>

第2种:对象模式

只须要批改src/router/index.js外面的路由,组件外面和布尔模式一样:

 { path: '/task-detail', name: 'task-detail', component: () => import('../views/task-detail.vue'), props: { taskId: '10000218' } },

通过路由给组件传值的时候要这样写:

<router-link to="/task-detail">工作详情10000217</router-link>

第3种:函数模式

只须要批改src/router/index.js外面的路由,组件外面获取值和下面两种一样:

 { path: '/task-detail/:taskId', name: 'task-detail', component: () => import('../views/task-detail.vue'), props: route => { if (route.params && route.params.taskId) { return { taskId: route.params.taskId } } } },

通过路由给组件传值的时候要这样写:

<router-link to="/task-detail/10000217">工作详情10000217</router-link>
  1. HTML5 History模式

讲这个之前,我先来介绍一个html中锚点的概念,就拿我的写着这个博客来说,一篇文章很长,超过你电脑一屏或者两屏的内容表,例如上面的文章内容:

题目1. 小标题12. 小标题23. 小标题3...

我想快速访问某个小标题的内容,能够这样来写

<a href="#title1">拜访小标题1</a><a href="#title2">拜访小标题1</a><a href="#title3">拜访小标题1</a>...<a name="title1">1. 小标题1</a><a name="title2">2. 小标题2</a><a name="title3">3. 小标题3</a><!--或则--><div id="title1">1. 小标题1</a><div id="title2">2. 小标题2</a><div id="title3">3. 小标题3</a>

这就是锚点的内容,这又和本节内容有什么关系呢?vue-router官网说,vue-router默认hash模式,hash是什么呢?我来介绍一下:

hash属性是一个可读可写的字符串,该字符串是URL的锚局部(从#好开始的局部),#代表网页中的一个地位,其右面的字符就是该地位的标识符(说的就是锚点),例如:

http://www.blog.com/index.html#title1

就代表网页index.html的title1地位,浏览器读取这个URL后会主动将title1地位滚动至可视区域

是用来领导浏览器动作的,对服务器端齐全无用,所以,HTTP申请中不包含#,比方拜访上面的网址:

http://www.blog.com/index.html#title1

浏览器理论收回的申请是这样的:

GET /index.html HTTP/1.1Host: www.example.com

能够看到只申请了index.html,基本没有#title1这部分

所以,在URL中,第一个#前面呈现的任何字符,都会被浏览器解读为地位标识符(锚点),这些字符都不会被发送到服务器端,而且扭转URL中#前面的局部,只相当于扭转了锚点,浏览器只会滚动到相应地位,不会从新加载网页,比方:

http://www.blog.com/index.html#title1到http://www.blog.com/index.html#title2

这种锚点的扭转,齐全由浏览器管制,而不会从新向服务器申请index.html这个页面

当初咱们再回到vue-router官网文档这里,它提到了,vue-router默认hash模式(#前面跟字符串)应用hash来模仿一个残缺的URL,于是当URL扭转时,页面不会从新加载,如果不想要这种形式展现,还能够用路由的history模式

const router = new VueRouter({ mode: 'history', routes})

这样,路由就变动了:

http://localhost:4000/#/task-detail/10000216变成了http://localhost:4000/task-detail/10000217

然而这种模式也须要后端的反对,因为咱们的利用是个单页客户端利用,只有一个index.html页面,当如有变动时,如果采纳hash模式,从路由:

http://localhost:4000/#/task-detail/10000217变到http://localhost:4000/#/about

时,不会从新申请页面,至始至终只有一个index.html页面,路由的变动,也能够看成是锚点的扭转,显相当于浏览器从#/task-detail这个锚点到/about这个锚点,然而如果采纳history模式,从路由:

http://localhost:4000/task-detail/10000217变成了http://localhost:4000/about

这个时候浏览器就会认为是须要向服务器申请task-detail.html和about.html这两个html的,然而服务器上基本没有这两个html,就会报404文件未找到谬误,所以这个时候就须要后端哥们的反对,未匹配到html页面的时候,就返回index.html这个页面,具体后端怎么配置,能够参考官网文档

  1. 导航守卫

这部分是vue-router局部,在理论我的项目开发中也很有用途,用来判断用户是否登录,或则有权限拜访某一个页面,它帮忙咱们在路由产生跳转到导航完结这个过程中做一些逻辑解决,我分几个局部来讲

第一种:全局前置守卫

来看下src/router/index.js里的router,这是vue-router的实例,它下面有一个beforeEach办法,性能就是进行全局前置守卫

咱们来看下如何应用:

import Vue from 'vue'import VueRouter from 'vue-router'import Home from '../views/Home.vue'import errorRoutes from './error-router'Vue.use(VueRouter)const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') }, { path: '/task-detail/:taskId', name: 'task-detail', component: () => import('../views/task-detail.vue'), props: route => { if (route.params && route.params.taskId) { return { taskId: route.params.taskId } } } }, { path: '/product', name: 'product', component: () => import('../views/product/index.vue'), children: [ { path: 'ele-product', // 子路由须要后面加'/',只有副路由才有 name: 'ele-product', component: () => import('../views/product/ele-product.vue'), children: [ { path: 'phone', // 子路由须要后面加'/',只有副路由才有 name: 'phone', components: { default: () => import('../views/product/phone.vue'), apple: () => import('../views/product/apple.vue'), mi: () => import('../views/product/mi.vue'), vivo: () => import('../views/product/vivo.vue'), }, }, { path: 'computer', // 子路由须要后面加'/',只有副路由才有 name: 'computer', component: () => import('../views/product/computer.vue'), } ] } ] }, ...errorRoutes, { path: '*', redirect: '/notFound' }]const router = new VueRouter({ mode: 'history', routes})const whitelist = ['login', 'error401', 'error500', 'notFound', 'compatible', 'notLogin', '404', 'abnormal']let app;router.beforeEach((to, from, next) => { // const isLogin = !!sessionStorage.getItem('accessToken'); const isLogin = true if (isLogin) { if (to.name === 'login') { next({ name: 'home' }); } else { next() } } else { if (whitelist.indexOf(to.name) !== -1) { next() } else { next({ name: 'login' }) } }});// next()办法肯定要加,不然不能跳转router.afterEach((to, from) => { app = document.querySelector('.app-content-inner') app && app.scrollTo(0, 0)})export default router

下面的代码很好读懂,我就不做一一介绍,大略逻辑就是从本地session拿token看是否曾经登录,登录了间接跳转到首页,没有登录,看看以后路由是否在白名单那里,不在间接跳转到登录页登录

第二种:全局后置钩子

router实例下的afterEach办法就是全局后置钩子,和前置守卫不同的是,这些钩子不会承受next函数,也不会扭转导航本省

第三种:路由独享的守卫

 { path: '/about', name: 'About', component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'), beforeEnter: (to, from, next) => { if (from.name === 'Home') { console.log('从home页跳转过来') } else { console.log('不是从home页跳转来的') } next() } },

第四种: 组件内守卫

beforeRouteEnter

提到这个的时候,想起来前几天做我的项目遇到一个问题,我有一个创立工作的页面(http://localhost:8082/#/web-task/task-create),别离能够从两个页面:监测工作列表(http://localhost:8082/#/web-task/web-list)和监测工作治理(http://localhost:8082/#/web-task/task-list)跳转过来,需要是,从哪个页面跳转过来的,当工作创立结束还回到哪个页面

惯例思路就是在创立工作的页面监听路由的变动,拿到from.path里的值,也就是上个页面的路由,然而怎么都监听不到路由的变动,这个时候我就想到了beforeRouteEnter应用组件内守卫,能够拿到to和from

beforeRouteEnter (to, from, next) { console.log(to.name) console.log(from.name) console.log(this) // undefined next()}

然而我发现在外面拿不到this这个vue实例,解释起因是因为:走这一步的时候,以后组件还没有渲染实现,vue实例还未创立实现,然而我非要应用肿么办?

解决办法就是给next函数传一个回调函数,完满解决这个问题

beforeRouteEnter (to, from, next) { next(vm => { if (from.name === 'web_list') { vm.from_router = '/web-task/web-list' } else if (from.name === 'task_list') { vm.from_router = '/web-task/task-list' } })}

beforeRouteLeave

对于这个的用法,比方用户在以后页面进行编辑操作,还没有保留就要跳转到其它页面,那么你就能够在这个钩子函数外面揭示用户,编辑还未实现,是否勾销编辑,这里提醒一下:在这个办法里能够间接用this

<script>export default { props: { taskId: { type: [String, Number], default: '' } }, data () { return { } }, methods: { }, beforeRouteLeave (to, from, next) { const leave = confirm('确定来到吗?') if (leave) { next() } else { next(false) } // next(vm => { //     console.log(vm) // vue实例 // }) }, beforeRouteUpdate (to, from, next) { console.log('组件被复用') next() }}</script>

beforeRouteUpdate

// 在以后路由扭转,然而该组件被复用时调用// 举例来说,对于一个带有动静参数的门路 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,// 因为会渲染同样的 Foo 组件,因而组件实例会被复用。而这个钩子就会在这个状况下被调用。// 能够拜访组件实例 `this`beforeRouteUpdate (to, from, next) { console.log('组件被复用') next()}

第五种 路由元信息

{ path: '/', name: 'Home', component: Home, meta: { title: '首页', requiresAuth: ['admin', 'user'] }},

这里的meta就是元信息,能够在这里给每个路由对象配一个title或者打一个标记,用来区别哪些用户能够拜访这个路由

接下来,我讲一个例子,利用前置守卫和路由元信息,批改window.document.title的值

首先找到咱们在第二节新建的src/lib/util.js,过后说了这个文件用来寄存和业务相干的办法,接下来咱们就新建一个办法

// util.jsexport const setTitle = (title) => { window.document.title = title ? title + '-拨测治理平台' : '拨测治理平台'}

而后在src/router/index.js外面引入,并且在前置守卫里减少一行代码

// router/index.jsimport {setTitle} from '@/lib/util'router.beforeEach((to, from, next) => { to.meta && setTitle(to.meta.title)})

第六种 过渡成果

路由切换的时候,在<router-view>外面加载页面,咱们能够利用<transition>组件给它增加一些过渡成果

<transition> <router-view></router-view></transition>

如果是多个视图,须要用<transition-group>包裹

<transition-group> <router-view></router-view> <router-view name="phone"></router-view></transition-group>

我来写一个过渡成果的例子:

<transition name="router"> <router-view/></transition><style lang="less">// 进入成果.router-enter { opacity: 0;}.router-enter-active { transition: opacity 1s ease;}.router-enter-to { opacity: 1;}// 来到成果.router-leave { opacity: 1;}.router-leave-active { transition: opacity 1s ease;}.router-leave-to { opacity: 0;}</style>