当程序加载执行完vue-router文件,就执行new VueRouter()动作以及new Vue()动作,本篇文章就是探究这两个过程对于路由具体的处理逻辑。
具体分析 new VueRouter()在第一篇文章中的实例中,调用new VueRouter()来创建router对象,VueRouter()具体的处理逻辑如下:
从上面的处理逻辑中可以看出,new VueRouter()创建路由对象就是初始化属性,而最为重要的就是mode模式问题了
mode模式支持’hash’、’history’、’abstract’,前两者是浏览器环境下,最后一个是支持Js的非浏览器环境
根据mode模式调用不同的构造函数生成不同的History对象,该对象是实现路由的核心对象之一
HashHistory构造函数实际在加载解析vue-router时就已经执行了HashHistory
该函数本身是一个立即执行函数,主要的功能就是定义HashHistory相关的方法和构造函数以及实现继承History
在源码中History是父对象,无论是HashHistory、HTML5History还是AbstractHistory都继承自History
创建HashHistory最主要的功能点就是执行History构造函数
// History$$1就是HistoryHistory$$1.call(this, router, base);而History对象定义了路由操作的最基本的操作,例如updateRoute等,而其构造函数就是定义需要用到的属性, 主要的属性如下:
router:当前路由对象base:基本路径current:当前路由模块,默认是START模块就是在加载解析是创建的path为/的默认路径 new Vue() new Vue({ router}).$mount('#app')这步操作实际上会调用VueRouter的beforeCreate生命周期函数
在上一篇文章中说了,在加载解析的过程中会调用install函数,而该函数中最重要的一点就是对所有组件全局混入beforeCreate和destroyed。
在new Vue()这步会调用beforeCreate生命周期函数,而这边是路由功能实现的触发点,具体看看该生命周期的处理逻辑:
beforeCreate: function beforeCreate () { // 如果当前Vue实例存在router配置属性 if (isDef(this.$options.router)) { // 当前Vue实例 this._routerRoot = this; // 当前VueRouter实例对象 this._router = this.$options.router; // 调用VueRouter.prototype.init this._router.init(this); // 定义响应属性_route Vue.util.defineReactive(this, '_route', this._router.history.current); } else { // 若父组件存在_routerRoot则当前Vue组件实例中_routerRoot与相同,否则就是当前Vue实例对象 this._routerRoot = (this.$parent && this.$parent._routerRoot) || this; } registerInstance(this, this); },这里Vue实例$options是存在router属性的,所以会调用VueRoute对象的init方法。
init实际上在上一章就分析了这边的处理逻辑,就是定义Vue根实例对象,以及history的处理,而history的处理是init很关键的地方。
var history = this.history;// history模式、hash模式的处理if (history instanceof HTML5History) { history.transitionTo(history.getCurrentLocation());} else if (history instanceof HashHistory) { var setupHashListener = function () { history.setupListeners(); }; history.transitionTo( history.getCurrentLocation(), setupHashListener, setupHashListener ); } history.listen(function (route) { this$1.apps.forEach(function (app) { app._route = route; }); });从上面的代码可以看出,实际上是调用history对象的transitionTo、listen方法,实际上可以猜测出这两个方法的大概的功能:
transitionTo:路由切换listen:监听当然还是要看看它们的具体实现逻辑,首先来看看trsitionTo方法。
transitionTo首先看看传递给transitionTo方法的参数,有三个:
history.getCurrentLocation()另外两个参数都是history.setupListeners()getCurrentLocation:是获取window.location.href中#之后的路径,就是路由路径
setupListeners:是监听popstate或hashchange事件
首先来看看setupListeners,这里是vue-router实现的核心
// 当前的history对象var this$1 = this;var router = this.router;// 切换新路由的页面滚动位置的处理,在支持pushState的前提下var expectScroll = router.options.scrollBehavior;var supportsScroll = supportsPushState && expectScroll;if (supportsScroll) { setupScroll();}// 监听popstate或hashchange事件window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', function () { // 获取当前路由 var current = this$1.current; if (!ensureSlash()) { return } // 路由切换 this$1.transitionTo(getHash(), function (route) { if (supportsScroll) { handleScroll(this$1.router, route, current, true); } // hash模式下,实际上是执行window.location.replace方法 if (!supportsPushState) { replaceHash(route.fullPath); } });});从上面可知在实现路由切换都涉及到了transitionTo方法,就看看transitionTo的实现逻辑。
transitionTo内部实际上是调用confirmTransition方法
confirmTransition方法主要的点就是:
判断是否是相同模块,做具体的处理
应用beforeRouterEnter等钩子
执行uploadRoute,更新当前路由
执行传入的函数,实现注册监听onhashchange事件
listen该方法就是替换Vue实例中的_route响应属性
总结上面的总结就是分析了整个大概流程,VueRouter的实现细节蛮多的,实际上通过上面的基本知晓VueRouter整个流程:
new VueRouter()实际上主要就是history对象的实现,该对象是实现路由操作的核心
new Vue()时会触发组件的beforeCreate生命周期,调用VueRouter的init方法,完成默认路由的切换以及路由的监听
下一篇文章会分析路由切换时的处理流程。