路由注册
改一下例子
main.js
1 | // The Vue build version to load with the `import` command |
App.vue
1 | <template> |
从Vue.use(VueRouter)开始吧
1 | function initUse (Vue) { |
这个方法接受一个 plugin 参数,并且有一个存储所有注册过的 plugin的数组,它会判断plugin是否有install方法,有就执行,然后把plugin添加到installedPlugins数组
走进install方法
1 | var _Vue; |
1)installed判断是否安装过
2)Vue.mixin把beforeCreate和destroyed钩子函数注册到组件上
3)把Vue.prototype上的$router和$route变成响应式对象
4)Vue.component定义了两个组件
VueRouter
看下创建router实例的时候做了什么
1 | const router = new VueRouter({ |
这个是VueRouter构造函数
1 | var VueRouter = function VueRouter (options) { |
1)this.options保留了传入的options参数
2)this.matcher表示路由匹配器
进入createMatcher方法
1 | function createMatcher ( |
首先走进createRouteMap方法,这里是创建一个路由映射表
1 | function createRouteMap ( |
走进去addRouteRecord方法
1 |
|
最后得到的就是 pathList、pathMap 和 nameMap。其中 pathList 是为了记录路由配置中的所有 path,而 pathMap 和 nameMap 都是为了通过 path 和 name 能快速查到对应的 RouteRecord。
3)this.fallback表示根据传入的配置参数,并且浏览器是否支持,初始化不同history实例
这个例子中的history实例

之后返回VueRouter的实例router
在new Vue的时候,走进init之后,触发beforeCreate生命周期钩子(在上面做mergeOptions操作(参数合并)的时候,为vm.$options添加(或合并)的钩子函数)

进入调用钩子函数
1 | beforeCreate: function beforeCreate () { |
进入this._router.init(this)
1 | VueRouter.prototype.init = function init (app /* Vue component instance */) { |
先进入transitionTo方法
1 | History.prototype.transitionTo = function transitionTo ( |
1)根据location与this.current找到匹配到目标的路径
2)进入confirmTransition方法做真正的切换,最后会执行传入的onComplete回调,其实最后调用的是history.setupListeners


1 | HashHistory.prototype.setupListeners = function setupListeners () { |
这里会根据浏览器支持和选择,监听popstate或者haschange事件,设置回调
init之后调用Vue中定义的defineReactive对_route进行劫持,这个劫持之前有说过,其实是执行的依赖收集的过程,执行_route的get就会对当前的组件进行依赖收集,如果对_route进行重新赋值触发setter就会使收集的组件重新渲染。(完成依赖收集在mount阶段,生成RouterView的vnode的时候收集依赖)
点击router-link组件试一下,执行了router.push(location,noop)

这个location参数在这里,组件render的时候获得的

走进push方法
1 | VueRouter.prototype.push = function push (location, onComplete, onAbort) { |

进入this.history.push方法
1 | HashHistory.prototype.push = function push (location, onComplete, onAbort) { |
进入transitionTo方法
1 | History.prototype.transitionTo = function transitionTo ( |
获取到跳转的路由对象,进入confirmTransition方法,最后触发传入的回调函数
1 | function () { |
进入updateRoute方法
1 | History.prototype.updateRoute = function updateRoute (route) { |
进入this.cb回调,遍历this.$1.apps,修改_route属性,触发setter,之后触发视图更新
1 | history.listen(function (route) { |
这个this.$1.apps是在init的时候往里面push值

最后调用回调函数

走进去pushHash
1 | function pushHash (path) { |
走进pushState
1 | function pushState (url, replace) { |

router-link与a标签的区别
router-link组件有自己的render方法,在这里添加了on的属性,之后生成DOM结构的时候就会为a标签添加

a标签点击的时候,进去handler方法

guardEvent判断是否event上有preventDefault,有就调用,阻止默认行为

总结
hash路由,根据情况使用的不同
1 | var supportsPushState = inBrowser && (function () { |
supportsPushState为true的时候:
use方法,调用install方法,为所有组件添加beforeCreate和destroyed钩子,还有定义了RouterView和RouterLink组件,并且让\$router和$route变成响应式
创建router实例,根据不同的支持实例化不同的history,还有个match方法匹配地址。
new Vue 进入beforeCreate钩子调用,调用transitionTo,根据location匹配出route,调用comfirmTransiton,之后添加监听事件popstate,渲染视图(routerlink渲染)
点击routerlink生成的a标签,触发点击事件,被阻止冒泡,之后调用router.push(location, noop); 进入history.push,又进入了transitionTo,调用dep.notify,之后调用 history.pushState({ key: _key }, ‘’, url); (比如”http://localhost:8080/#/foo")之后更新搜索栏地址。
更新视图
supportsPushState为false的时候:
use方法,调用install方法,为所有组件添加beforeCreate和destroyed钩子,还有定义了RouterView和RouterLink组件,并且让\$router和$route变成响应式
创建router实例,根据不同的支持实例化不同的history,还有个match方法匹配地址。
new Vue 进入beforeCreate钩子调用,调用transitionTo,根据location匹配出route,调用comfirmTransiton,之后添加监听事件hashchange,渲染视图(routerlink渲染)。
- 点击routerlink生成的a标签,触发点击事件,被阻止冒泡,之后调用router.push(location, noop); 进入history.push,又进入了transitionTo,调用dep.notify,之后调用pushHash(route.fullPath),因为supportsPushState为false所以执行的是window.location.hash = path(比如path是/foo) ,之后更新搜索栏地址。也触发了hashchange事件
- 更新视图