前言
当程序加载执行完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就是History
History$$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方法,完成默认路由的切换以及路由的监听
下一篇文章会分析路由切换时的处理流程。
————————————————
智一面|前端面试必备练习题