前言

当程序加载执行完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方法,完成默认路由的切换以及路由的监听

下一篇文章会分析路由切换时的处理流程。

Logo

前往低代码交流专区

更多推荐