js路由—backbone的路由的实现01

backbone的路由分两部分。其中一个是路由配置router,另外一个是和路由相关的history,用作浏览器的前进后退等。

先看下histroy部分。

1 首先,初始化路由配置数组,然后绑定checkurl上下文对象是backbone

this.handlers = [];
_.bindAll(this, 'checkUrl');

2 全局对象是window的情况

// Ensure that `History` can be used outside of the browser.
    if (typeof window !== 'undefined') {
      this.location = window.location;
      this.history = window.history;
    }

3 缓存几个正则,第一个是#或 / 开头, 或空白结尾; 第二个是 /开头 或 / 结尾; 第三个是 / 结尾

  // Cached regex for stripping a leading hash/slash and trailing space.路由修正,开头的/,#或结尾的空格
  var routeStripper = /^[#/]|s+$/g;

  // Cached regex for stripping leading and trailing slashes.跟路径修正
  var rootStripper = /^/+|/+$/g;

  // Cached regex for detecting MSIE.
  var isExplorer = /msie [w.]+/;

  // Cached regex for removing a trailing slash.末尾的/修正
  var trailingSlash = //$/;

4 设置默认状态是路由未开启

History.started = false;

下面是原型的一些关键方法

History.prototype = {
    interval: 50,
    atRoot: function() {
      return this.location.pathname.replace(/[^/]$/, '$&/') === this.root;
      //判断pathname后面加上/, 与this.root是否是全等的,是就说明在跟目录下
    },
    getHash: function(window){
      var match = (window || this).location.href.match(/#(.*)$/);
          return match ? match[1] : '';
    },
getFragment: function(fragment, forcePushState){
      if(fragment === null){
        if(this._hasPushState || !this._wantsHashChange || forcePushState){
          //支持pushState, 或hashchange, 或强制性的支持pushState
          fragement = this.location.pathname; //取出url的pathname就是fragment
          var root = this.root.replace(trailingSlash, '');//root去掉尾部的/
          if(!fragement.indexOf(root)){
            fragement = fragement.substr(root.length);//如果fragment中没有root路径,那就让出前面的root路径的长度的位置????这块还有疑问
          }
        }else{
          fragement = this.getHash();//没提供参数,并且不支持pushState,则取到#锚点
        }
      }
      return fragement.replace(routeStripper, '');//提供fragment参数,则进行路由修正
    }

1 原型中其他条件准备好,开始监听hash变化

    start: function(options){
      if (History.started) throw new Error("Backbone.history has already been started");
      History.started = true;
      //如果历史纪录已经处于开启状态,抛出错误。不只跟实例有关,还与构造函数本身的属性相关

      this.options = $.extend({root: '/'}, this.options, options);
      //扩展配置,设置默认root是/,扩展this的配置以及start传入的配置
      this.root = this.options.root;
      this._wantsHashChange = this.options.hashChange !== false;
      //hashChange不明显设置为false,则默认想要hashChange
      this._wantsPushState = !! this.options.pushState;
      this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState)
      //是否支持pushState,取决于设置,并且是否有浏览器的历史纪录支持
      var fragment = this.getFragment();
      var docMode = document.documentMode;//documentMode是一个IE的私有属性,在IE8+中被支持。
      
      this.root = ('/' + this.root + '/').replace('rootStripper', '/');//给root加上前后/并且修正为只有一个

      if(this._hasPushState){
        $(window).on('popstate', this.checkUrl);//支持pushState
      } else if(this._wantsHashChange && ('onhashchange' in window) && !oldIE){//支持hashchange
        $(window).on('hashchange', this.checkUrl);
      } else if(this._wantsHashChange){
        this._checkUrlInterval = setInterval(this.checkUrl, this.interval);//都不支持
      }

      this.fragement = fragement;
      var loc = this.location;

      // If we've started off with a route from a `pushState`-enabled browser,
      // but we're currently in a browser that doesn't support it...
      // 兼容性处理 参数设置与当前浏览器支持情况冲突的时候
      if(this._wantsHashChange && this._wantsPushState){//既需要hashchange也需要pushstate
          if(!this._hasPushState && !this.atRoot()){
            //不在跟目录,并且不支持pushState
            this.fragement = this.getFragment(null, true);
            //取出fragment,并且强制要求pushstate
            this.location.replace(this.root + '#' + this.fragement);
            //给出地址,让浏览器自己处理
            return true;
          }else if(this._hasPushState && this.atRoot() && loc.hash){
            //如果是锚点导航并且在跟目录里面的,用锚点
            this.fragement = this.getHash().replace(routeStripper, '');
            this.history.replaceState({}, document.title, this.root + this.fragement);
          }
      }
      if(!this.options.silent){
        return this.loadUrl();
      }
    }

2 停止history监听

// 停止历史支持
    stop: function() {
       $(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
      clearInterval(this._checkUrlInterval);
      History.started = false;
    },

3 添加路由映射

 // 导航到相应的route地址。。。添加fragment改变时候,需要检查的路由。
    // 这里用handlers队列处理, 防止快速的改变地址但是没处理完成 引起的问题
    route: function(route, callback) {
      this.handlers.unshift({route: route, callback: callback});
    },

4 检查url是否改变,改变则loadurl

// 检查url 兼容性处理
    checkUrl: function(e) {
      var current = this.getFragment();
      if (current === this.fragment) return false;
      this.loadUrl() || this.loadUrl(this.getHash());
    },
// load当前的URL片段 如果真的有相应的route地址处理函数 则执行它
    loadUrl: function(fragmentOverride) {
      var fragment = this.fragment = this.getFragment(fragmentOverride);
      
     var matched =this.handlers.some(function(handler, index, array) {
        if (handler.route.test(fragment)) {
          handler.callback(fragment);
          return true;
        }
      });
      return matched;
    },

5 设置导航,更新hash

// 导航 根据url片段导航去相应的画面 兼容性处理
    navigate: function(fragment, options) {
      if (!History.started) return false;
      if (!options || options === true) options = {trigger: options};
      fragment = this.getFragment(fragment || '');
      if (this.fragment === fragment) return;
      this.fragment = fragment;
      var url = this.root + fragment;

      // If pushState is available, we use it to set the fragment as a real URL.
      if (this._hasPushState) {
        this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);

      // If hash changes haven't been explicitly disabled, update the hash
      // fragment to store history.
      } else if (this._wantsHashChange) {
        this._updateHash(this.location, fragment, options.replace);

      // If you've told us that you explicitly don't want fallback hashchange-
      // based history, then `navigate` becomes a page refresh.
      } else {
        return this.location.assign(url);
      }
      if (options.trigger) this.loadUrl(fragment);
    },

    // Update the hash location, either replacing the current entry, or adding
    // a new one to the browser history.
    // 更新hash值 包含替换当前hash 或者是增加历史到浏览器的历史记录中
    _updateHash: function(location, fragment, replace) {
      if (replace) {
        var href = location.href.replace(/(javascript:|#).*$/, '');
        location.replace(href + '#' + fragment);
      } else {
        // Some browsers require that `hash` contains a leading #.
        location.hash = '#' + fragment;
      }
    }

  

原文地址:https://www.cnblogs.com/jingwensophie/p/4863887.html