美文网首页
vue路由更新没有触发事件监听分析

vue路由更新没有触发事件监听分析

作者: 木子小寉鸟 | 来源:发表于2020-05-20 13:18 被阅读0次

一、疑问


hash模式

项目启动后会监听路由变化,监听到变化后调用transitionTo,那么push和replace等方法调用transitionTo去更新视图,然后去更新路由,那这次路由变更后触发了路由监听事件,又一次调用了transitionTo,那这个场景是调用了多次视图和路由更新吗?

二、popstate和hashchange


在我们学习vueRouter源码的时候发现,路由变化的监听是通过popstate或者hashchange来监听路由变化的,但是在我们的使用中发现,不同版本处理的效果不太一致,比如:在3.1.6版本通过push等方法更新路由的时候,不会触发hash中路由监听事件,但是在3.0.3版本中,是会触发的路由监听事件的。

那么,导致这些的原因到底是什么呢?首先我们来分析下两种事件监听的特点

popstate事件特点

调用history.pushState()或history.replaceState()不会触发popstate事件。只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用history.back())
popstate事件监听

hashchange事件特点

只要锚点也就是hash值有更新,就会触发hashchange监听事件
hashchange事件监听

在我们了解了两种路由监听事件的特点后,再去分析源码就简易多了。

三、源码摘取


// 浏览器判断是否支持pushstate方法,若支持,返回true调用pushstate等方法,若不支持,则使用window.location去更新 FIXME:该方法是vueRouter@3.1.6版本写法
var supportsPushState =
  inBrowser &&
  (function () {
    var ua = window.navigator.userAgent;

    if (
      (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
      ua.indexOf('Mobile Safari') !== -1 &&
      ua.indexOf('Chrome') === -1 &&
      ua.indexOf('Windows Phone') === -1
    ) {
      return false
    }
    // 谷歌浏览器return true
    return window.history && 'pushState' in window.history
  })();
 
// 是否支持popState方法浏览器判断 该方法是vueRouter@3.0.3版本写法
var supportsPushState = inBrowser && (function () {
    var ua = window.navigator.userAgent;
    
    // FIXME:与316版本相比多出来的判断 所以谷歌浏览器访问return false,即不支持pushstate方法
    if (ua.indexOf('Chrome') !== -1) {
        return false
    }

    if (
        (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
        ua.indexOf('Mobile Safari') !== -1 &&
        ua.indexOf('Chrome') === -1 &&
        ua.indexOf('Windows Phone') === -1
    ) {
        return false
    }

    return window.history && 'pushState' in window.history
})();

// hash监听方法 若支持pushstate则监听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);
      }
      if (!supportsPushState) {
        replaceHash(route.fullPath);
      }
    });
  }
);

// 更新路由方法 若支持pushstate则调用history.pushstate 若不支持则调用window.location去更新路由
function pushHash(path) {
  if (supportsPushState) {
    pushState(getUrl(path));
  } else {
    window.location.hash = path;
  }
}

四、根据不同vueRouter版本解析源码

谷歌浏览器、hash模式

vueRouter@3.1.6

这种情况路由变化不会触发事件监听,所以我们调用push方法只触发一次transitionTo方法,只更新一次视图

  • 谷歌浏览器支持pushstate, supportsPushState值为true
  • supportsPushState为真值,所以pushHash最终调用的是pushState
  • supportsPushState为真值,所以路由监听的事件是popstate
  • 基于popstate事件监听的特点,只有操作浏览器前进后退路由更新才会触发,而push等方法最终调用的是historyApi操作的路由更新,所以在这个版本下,是不会触发popstate事件监听的
vueRouter@3.0.3

这种情况路由变化就会触发事件监听,所以我们调用push方法会触发两次transitionTo方法,但是由于我们在confirmTransition中有相同路径就不继续往下走的拦截,所以,最后只会更新一次视图

  • 谷歌浏览器不支持pushstate, supportsPushState值为false
  • supportsPushState为假值,所以pushHash最终调用的是window.location.hash
  • supportsPushState为假值,所以路由监听的事件是hashchange
  • 基于hashchange事件监听的特点,只要hash值变化就会触发,所以push等方法最后调用的window.location.hash方法触发了hashchange监听的事件

五、总结

依据我们上边的分析,就能够完全的解释了为啥在vueRouter不同版本触发事件监听的效果不一致,主要就是因为在不同版本,supportsPushState判断的方法不一致导致的。当然不管是哪种情况,不管我们调了一次还是两次的transitionTo方法,在最后,我们都只会更新一次视图。
当然,3.1.6版本的处理更合理一些,也就是使用pushstate去更新路由。希望大家能够通过这个小问题,让自己在开发过程中多注意些,是否把测试代码搞上去了。

相关文章

网友评论

      本文标题:vue路由更新没有触发事件监听分析

      本文链接:https://www.haomeiwen.com/subject/pjbgohtx.html