js路由—backbone的路由的實現01

backbone的路由分兩部分。其中一個是路由配置router,另一個是和路由相關的history,用做瀏覽器的前進後退等。javascript

先看下histroy部分。java

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 設置默認狀態是路由未開啓ide

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變化ui

    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監聽this

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

3 添加路由映射url

 // 導航到相應的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;
      }
    }
相關文章
相關標籤/搜索