vue插件

2019-02-13  本文已影响0人  百里哈哈

vuex

函数过程
new Store() -> (resetStoreVM)installModule -> (getNamespace, registerMutation, registerAction, registerGetter) makeLocalContext ->(dispatch、commit、getters、state)

resetStoreVM -> (wrappedGetters, new Vue(…))


vue-router

导航守卫中为什么需要手动触发next
其主要代码如下

function runQueue (queue, fn, cb) {
  var step = function (index) {
    if (index >= queue.length) {
      cb();
    } else {
      if (queue[index]) {
        fn(queue[index], function () {
          step(index + 1);
        });
      } else {
        step(index + 1);
      }
    }
  };
  step(0);
}

通过runQueue触发守卫队列函数

var iterator = function (hook, next) {
    if (this$1.pending !== route) {
      return abort()
    }
    try {
      hook(route, current, function (to) {
        if (to === false || isError(to)) {
          // next(false) -> abort navigation, ensure current URL
          this$1.ensureURL(true);
          abort(to);
        } else if (
          typeof to === 'string' ||
          (typeof to === 'object' && (
            typeof to.path === 'string' ||
            typeof to.name === 'string'
          ))
        ) {
          // next('/') or next({ path: '/' }) -> redirect
          abort();
          if (typeof to === 'object' && to.replace) {
            this$1.replace(to);
          } else {
            this$1.push(to);
          }
        } else {
          // confirm transition and pass on the value
          next(to);
        }
      });
    } catch (e) {
      abort(e);
    }
  };

通过iterator实现一个迭代器
runQueue方法调用

runQueue(queue, iterator, function () {
    var postEnterCbs = [];
    var isValid = function () { return this$1.current === route; };
    // wait until async components are resolved before
    // extracting in-component enter guards
    var enterGuards = extractEnterGuards(activated, postEnterCbs, isValid);
    var queue = enterGuards.concat(this$1.router.resolveHooks);
    runQueue(queue, iterator, function () {
      if (this$1.pending !== route) {
        return abort()
      }
      this$1.pending = null;
      onComplete(route);
      if (this$1.router.app) {
        this$1.router.app.$nextTick(function () {
          postEnterCbs.forEach(function (cb) { cb(); });
        });
      }
    });
  });

有runQueue方法可知, iterator即为指向runQueue中的fn参数
iterator方法中的hook(route, current, function (to) {…})即为触发的钩子函数
所以当触发钩子函数而不执行function (to){}这个方法时,也就没有触发iterator中的next方法。此时导航守卫钩子函数队列的执行被终止。

vue-router有哪几种导航钩子

在回答这个问题之前我先多说几句做个铺垫
先说钩子函数吧,钩子函数在前端是个神奇的存在,在框架模型的生命周期中的不同有时候你想做一些不可描述的事情,怎么办那就注册回调呗。而钩子函数用来挂载这些回调的。

再说生命周期吧,个人意见呀,前端开发多涉及到视图变换,而视图变换的过程可以将其理解为一个生命周期

接下来说说路由这件事情。 举个栗子,假设存在 a, b两个路径 有a切换到B这是一个过程在, 在这个过程中涉及到A的离开 B的进入,A离开前、离开后、B进入前进入后,我们希望在这些节点可以做一些事情,这时就要考虑设计自身的钩子函数; 我们有时候也会希望 在进入A的过程中、进入B的过程中都调用同样的方法,那么全局钩子就应运而生了。

在窥探源码可知,实现导航队列时,会将这些钩子函数进行封装,然后放入queue这个变量中。
queue的主要代码如下所示

var queue = [].concat(
    // in-component leave guards
    extractLeaveGuards(deactivated),
    // global before hooks
    this.router.beforeHooks,
    // in-component update hooks
    extractUpdateHooks(updated),
    // in-config enter guards
    activated.map(function (m) { return m.beforeEnter; }),
    // async components
    resolveAsyncComponents(activated)
  );
var queue = enterGuards.concat(this$1.router.resolveHooks);

vue-router的导航守卫 包括全局守卫 beforeenter, beforeresolved, afterEnter 独立路由守卫 beforeenter ; 还有就是组件级的包括 beforerouterenter 、 beforerouterupdated、 beforerouterleaved ; 从思路上来说大概就这些。

然后接下来在举过例子阐述这些钩子发生的过程

还用AB这两个导航来说吧 。 第一种情况 我们首次进入A 发生的过程是 beforeenter- beforeenter-beforerouterenter-beforesolved-模板更新-afterenter ;

第二中情况有A切换进入B 多了一个A离开时的回调

第三种情况是由B的参数改变而产生的一系列的变化 beforeenter—beforerouterupdated-beforesolved-模板更新-afterenter ;

路径切换流程
init -> transitionTo -> confirmTransition -> updateRoute -> afterHooks
push -> transitionTo ->pushHash -> pushState、getUrl -> saveScrollPosition

监听浏览器路径切换
setupListeners -> window.addEventListener(supportsPushState ? ‘popstate’ :’hash change’

** popstate事件**
当活动历史记录条目更改时,将触发popstate事件。
需要注意的是调用history.pushState()或history.replaceState()不会触发popstate事件。只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用history.back())

window.addEventListener('popstate', function (e) {
      var current = this$1.current;

      // Avoiding first `popstate` event dispatched in some browsers but first
      // history route not updated since async guard at the same time.
      var location = getLocation(this$1.base);
      if (this$1.current === START && location === initLocation) {
        return
      }

      this$1.transitionTo(location, function (route) {
        if (expectScroll) {
          handleScroll(router, route, current, true);
        }
      });
    });

ensureURL函数
当前路径不等于getHash时所进行的路径操作

HashHistory.prototype.ensureURL = function ensureURL (push) {
    var current = this.current.fullPath;
    if (getHash() !== current) {
      push ? pushHash(current) : replaceHash(current);
    }
  };

replaceHash->replaceState、 getUrl -> pushState -> saveScrollPosition(history.pushState)-> getStateKey ->

路由切换视图变更

在路由view组件渲染时会获取其父元素上挂载的$router对象,在初始化阶段对该对象进行自动监听的绑定。
当改值发生变化时会引起其父组件的重新渲染
主要代码如下

Vue.mixin({
    beforeCreate: function beforeCreate () {
      if (isDef(this.$options.router)) {
        this._routerRoot = this;
        this._router = this.$options.router;
        this._router.init(this);
        Vue.util.defineReactive(this, '_route', this._router.history.current);
      } else {
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this;
      }
      registerInstance(this, this);
    },
    destroyed: function destroyed () {
      registerInstance(this);
    }
  });
Object.defineProperty(Vue.prototype, '$route', {
    get: function get () { return this._routerRoot._route }
  });
this$1.updateRoute(route);
var handler = function (e) {
      if (guardEvent(e)) {
        if (this$1.replace) {
          router.replace(location);
        } else {
          router.push(location);
        }
      }
    };

location指向当前link对应的路由对象
window.history 与 hash模式

1.hash ---- 利用URL中的hash(“#”)
2.利用History interface在 HTML5中新增的方法

window.history是用来保存用户在一个会话期间的网站访问记录,并提供相应的方法进行追溯。其对应的成员如下:
方法:back()、forward()、go(num)、pushState(stateData, title, url)、replaceState(stateData, title, url)
属性:length、state
事件:window.onpopstate
back():回退到上一个访问记录;
forward():前进到下一个访问记录;
go(num):跳转到相应的访问记录;其中num大于0,则前进;小于0,则后退;
pushState(stateData, title, url):在history中创建一个新的访问记录,不能跨域,且不造成页面刷新;
replaceState(stateData, title, url):修改当前的访问记录,不能跨域,且不造成页面刷新;

值得注意的是,通过pushState新增或者修改的history记录,被访问时,当前页面不刷新。而locaiton.href生成的记录被访问时,页面将进行刷新。
浏览器history变化图示

history
上一篇 下一篇

猜你喜欢

热点阅读