zoukankan      html  css  js  c++  java
  • 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;
          }
        }

      

  • 相关阅读:
    力扣题解 125th 验证回文字符串
    力扣题解 242th 有效的字母异位词
    力扣题解 387th 字符串中的第一个唯一字符
    Ubuntu
    Ubuntu
    Ubuntu
    ubuntu
    go-vscode-ubuntu20.04安装部署
    go-vscode-windows安装部署
    2020年任务
  • 原文地址:https://www.cnblogs.com/jingwensophie/p/4863887.html
Copyright © 2011-2022 走看看