前言
这次真的是太久太久没见了,伙伴们。 学习真的没有松懈,只是季度末要冲一冲绩效,一下子把时间都充值了。(ps:我看应该是我的脑子需要充值)
系列文章: Vue-Router源码分析之index.js
正文
为什么要从API中看门道呢?
我们在使用vue-router的时候,其实就是按照API进行操作,源码其实就是API的另一种形式,在我的学习中,我发现一味的干货有时效果并不好,由其是深入思考性比较强的内容。因此本篇文章所以作为一个过渡章节,让我们一起聊一聊承上启下的内容,聊聊API:
我们的push有哪些情况?
在实际工作中,编程式导航要比<router-link>标签使用的多的多,复杂逻辑中嵌套路由跳转都是要使用编程式导航。
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})
router.push({ path: `/user/${userId}` })
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
在编程式路由中,我们可以接收一个对象、一个字符串,对象的key有path、name、query、params等等,但是需要相互组合。
这么多情况vue-router要对push这么多情况做一个处理,我们push接收的参数的类型是什么?用flow设定为Location、RawLocation
用flow.js设定的类型
declare type Location = {
_normalized?: boolean;
name?: string;
path?: string;
hash?: string;
query?: Dictionary<string>;
params?: Dictionary<string>;
append?: boolean;
replace?: boolean;
}
declare type RawLocation = string | Location
翻译过来就是未处理的、生的路径。所以我们的push操作传递的是一个生冷的路径,经过一次match的处理
源码内容:
const route = this.router.match(location, this.current)
变成vue-router所需要的结构,route类型
declare type Route = {
path: string;
name: ?string;
hash: string;
query: Dictionary<string>;
params: Dictionary<string>;
fullPath: string;
matched: Array<RouteRecord>;
redirectedFrom?: string;
meta?: any;
}
所以从API结合表层源码来看,我们在进行编程式导航时的过程,先将未处理的路径(ps:vue-router对这种未处理路径没法直接进行跳转等操作)处理成一个规范的route类型,然后进行具体的路由跳转内容。
命名路由与视图
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
一个组件内可以有多个router-view组件,我接触的几个项目都是维护一个根结点的router-view。大部分都是这种结构
<template>
<div id="app">
<router-view/>
</div>
</template>
我们一个路径下可以展示多个路由视图组件吗?
答案当然是肯定的,官方的命名视图的例子(jsfiddle.net/posva/22wgk…
可以看到,我们在同一个路径下可以放置多个<router-link>,这里面的name与我们在构造vue-router时设置的components有对应,我们一般都是component : xxx;对应一个组件。如何对应多个的呢?
源码内容:
components: route.components || { default: route.component },
其实人家本身希望你用components,你要只用一个人家就给你包装成一个map,key呢就是默认值的意思。
再看看我们的vue-router2开始的路由守卫
路由守卫有哪些呢?
-
1:全局的beforeEach
-
2:单个路由配置的beforeEnter
-
3:全局的afterEach
-
4:组件内的路由守卫:
beforeRouteEnter
beforeRouteUpdate (2.2 新增)
beforeRouteLeave
-
5:全局的beforeResolve(vue-router2.5新增)
别的守卫还有吗?这么看来是没有了。
全局的beforeEnter、afterEach、beforeResolve为什么会作用在每一个路由配置中呢?在index一章中,我们有三个存放守卫的数组大家还有印象吗?
beforeHooks: Array<?NavigationGuard>;
resolveHooks: Array<?NavigationGuard>;
afterHooks: Array<?AfterNavigationHook>;
在router实例中存储这些。所以我们在做路由跳转的时候可以拿到这些守卫,其余的守卫怎么办?只好在跳转的时候具体情况具体执行,
守卫分为三个种类:
1:离开组件之前,2:进入组件(前后都有守卫) 3:更新组件之前
所以一个路由的跳转一定伴随着以下几个步骤
-
1:跳转我们称之为transitionTo
-
2:那么跳转中进行一个confirmTransiton(跳转前的准备)
-
3:跳转结束时进行一个updateRoute的过程
上文提到的confirmTransition:先收集到所有的守卫,把前置守卫们(前置!!!)连接成一个数组,跳转前挨个执行,前置守卫执行完怎么办?都通过来,那就更新当前路由,更新完当前路由就执行一下后置守卫。 vue-router源码中:
核心的路由跳转方法就是transitionTo: 具体内容是由confirmTransition与updateRoute实现的

其余的一些这这那那的容错,并不影响主流程。
总结一下
对于push、replace传递的未处理的路径,进行处理成vue-router可以操作的路径。
对于多个router-view,我们可以选择children,components来实现。
路由守卫为什么可以有全局的,每个路由都可以用,因为在router实例上,我们每次跳转都可以找到。
路由跳转经历了confirm确认与update更新两步
结束语
中秋节刚刚过去,十一马上就到了
没有来得及给大家带来祝福,希望大家十一的时候每次路由跳转都不堵车!
每一个前端er(boy and girl) 你们都不是一个人在战斗
我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,已经我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~
