文章以图片中代码注释为主,文字只是描述大致思路。可能有些非常细节的地方没有分析,因为分析VueRouter源码主要还是为了研究为什么router-link,router-view与前端路由之间的关联。搞清楚VueRouter的工作方式。具体的细节问题在对整个VueRouter有了了解之后,可以再结合工作中的实际问题来处理。
插件注册
老生常谈的问题了,在Vuex分析的文章中,就有插件功能的分析(juejin.cn/user/208432…
Vue.use
install
1. 为install函数添加静态属性installed,以此来防止插件多次安装
2. 调用Vue.mixin,全局混入beforeCreate与destoryed方法。
2.1在beforeCreate方法中通过组件之间的父子关系,让根组件(app.vue)的后代们都可以访 问到根组件的VueRouter实例。
2.2 调用VueRouter实例的init方法。
2.2 利用Vue.util.defineReactive方法向根组件添加_route属性,其值为Vue应用当前的路 由地址,如此一来根据【当前路由地址】的变化来驱动视图改变。
3. 在Vue的原型链上添加route属性,两个属性都为【只读】属性。其中route当前路由地址。
4. 注册RouterView,RouterLink组件
5. beforeRouter/beforeRouteLeave/beforeRouterUpdate的合并策略与created的合并策略一致。
this._router.init(this)
VueRouter自带对滚动轴的控制功能,init主要做的事情是:1)对scroll事件进行监听;2)可以向多个组件传入router,但是要保证不会重复监听scorll事件。
new VueRouter
直接看构造函数吧,最主要是是createMatcher方法与history实例。
1. VueRouter路由模式一般来说是由两种hash模式与html5模式,当然,这个是在浏览器端。在非浏览器环境下还有abstract模式。
2. createMatcher方法。返回addRouters与matcher方法。这两个是VueRouter最核心的方法。
createMatcher
在分析createMatcher方法之前,先给几个基本信息。有了这些信息会更好地了解VueRouter是如何工作的。
Vue全家桶的开发都用了Facebook的flow,下面就来看几个类型。
在我分析VueRouter源码的时候有写地方看着很迷糊,又是record又是location的。最后我发现这些都是有不同用处的。而类型也都在flow内声明了。
这里最值得注意的是RouteRecord类型(对应源码中的变量为record)含有components属性,其值肯定是Vue组件。
Route类型(对应源码中的变量为route)含有matched属性,其值为一连串具有父子关系的record组成的数组。
RawLocation(对应源码中的变量为raw)是字符串或者Route类型,为路由跳转的目的地。
现在正式分析createMatcher方法
1. 将用户自定义的路由传入createRouteMap函数得到,所有自定义路由path组成的数组pathList;path为key,route为值组成的pathMap;name为key,route为值组成的nameMap
2. 定义 addRoutes方法,其核心就是调用createRouteMap方法(传入旧的pathList,pathMap,nameMap)。
3. 定义 match方法
1. createRouteMap 与 addRouteRecord
1. createRouteMap会遍历用户自定义路由数组调用addRouteRecord方法。
2. 用户可以传入通配符*,保证*出现在路径数组pathList的最后
addRouteRecord
1. 标准化用户自定义路径
2. 定义record对象,其含有components对象,一般我们都会在自定义路由内使用component而非components,如果写了component,会被转换为coomponents,其key为default,value为用户引入的组件
3. 如果自定义路由有children属性就遍历子元素递归调用addRouteRecord函数
4. pathMap对象,pathList数组添加元素(同样的key不会被二次加入)
5. 如果自定义了alias的值,那么就把用户自定义的alias当成path,再执行一遍addRouteRecord函数
6. 如果用户自定义了name的值,就往nameMap属性内添加元素(相同的name不会被二次加入,如果非生产环境下,会提示重复添加相同名称的路由,这里只是提示,其实根本不用处理,因为在官方issure下看见有人提了,所以特地看了一下这方面的源码,其实只要开发中保证每一个路由地址都有独一无二的name值,就不会出现问题。issure地址:)
7. 函数是没有返回值的,因为传入的pathList,pathMap,nameMap都是引用对象
2. addRoutes
pathList,pathMap,nameMap为引用对象,其值为初次创建路由时会产生的。
用词方法我们可以动态添加路由,动态添加路由的时候会出现重复添加名称相同路由的问题,第一部分已经详细说过,不再赘述。
3. match
match方法有三个参数,分别是要跳转到路径,现在的路径,重定向参数
1. normalizeLocation根据跳转路径与现在路径获取标准化的目标路径,其为Location类型
2. 如果要跳转的标准化路径对象有name属性就直接获取name对应的record并创建route类型的对象返回
3. 没有name属性就获取path对应的record并创建爱你route类型的对象返回
4. 如果既没有name也没有path就返回一个空的route类型对象
注:跳转的时候可以传入name属性,name优先级高于path/alias
4. _createRoute与createRoute
上小节提到根据record常见route类型的对象,正是利用了_createRoute函数。
redirect与alias就先略过了,重点 看createRoute方法
createRoute方法中最需要关注的route的matched属性,如果有match函数执行后匹配到的record对象,那么就会调用formatMatch函数返回值并赋给matched属性。
5. formarMatch
其实formatMatch很简单,在用户定义的routes对象内都有父子关系,record之间也会建立斧子关系,那么route类型的matched属性的值就是一个record类型组成的数组,数组index从0是祖先,最后一个是匹配到的record。
这个伏笔在RouterView组件会再次出现。
最后
文章大概会分3-4个篇。
这篇先介绍了一个组件的安装与初始化。介绍了Location,record,raw,route的类型,为后续的分析打下基础。
随后分析了addRoutes与match方法。知道了addRoutes其实就是往pathList,pathMap,nameMap对象添加用户自定义路由;match就是根据传入的目标路由与现在的路径获取route对象,其中牵扯到了location,record,raw,route几个类型的值,在有前面的类型介绍之后会容易理解很多。
下篇文章会分析一下history类。