第四章:控制器和状态《Developing JavaScript Web Applications》学习笔记

模块模式

模块模式是用来封装逻辑并避免全局命名空间污染的好方法。使用匿名函数可以做到这一点,匿名函数也是JavaScript 中被证明最优秀的特性之一。通常是创建一个匿名函数并立即执行它。在匿名函数里的逻辑都在闭包里运行,为应用中的变量提供了局部作用域和私有的运行环境:

(function(){
/* ... */
})();

在执行这个匿名函数之前,我们用一对括号() 将它包起来。这样才能让JavaScript 解释器正确地将这段代码解析为一个语句。

全局导入和导出

可以将页面的window 导入我们的模块,直接给它定义属性,通过这种方式可以暴露全局变量:

var exports=this;
(function($){
    exports.Foo = "wem";
})(jQuery);

这里我们使用的变量名叫exports,用它来暴露全局变量,这样代码看起来更干净易读,可以直接看出模块创建了哪些全局变量。

一个完整的控制器

(function($, exports){
      var mod = {};
  mod.create = function(includes){
    var result = function(){
      this.init.apply(this, arguments);
    };
    
    result.fn = result.prototype;
    result.fn.init = function(){};
    
    result.proxy = function(func){ return $.proxy(func, this); };
    result.fn.proxy = result.proxy;

    result.include = function(ob){ $.extend(this.fn, ob); }; 
    result.extend = function(ob){  $.extend(this, ob); };
    if (includes) result.include(includes);
    
    return result;
  };
  
  exports.Controller = mod;
})(jQuery, window);

var exports = this;

$(function($){
  exports.SearchView = Controller.create({
    elements: {
      "input[type=search]": "searchInput",
      "form": "searchForm"
    },
    
    init: function(element){
      this.el = $(element);
      this.refreshElements();
      this.searchForm.submit(this.proxy(this.search));
    },
    
    search: function(){
      alert("Searching: " + this.searchInput.val());
      return false;
    },
    
    // Private
    
    $: function(selector){
      return $(selector, this.el);
    },
    
    refreshElements: function(){
      for (var key in this.elements) {
        this[this.elements[key]] = this.$(key);
      }
    }
  });
  
  new SearchView("#users");
});</code></pre>

See the Pen CaEbA by buer (@buer) on CodePen.

状态机

考虑这样一个场景,应用中存在一些视图,它们的显示是相互独立的,比如一个视图用来显示联系人,另一个视图用来编辑联系人。这两个视图一定是互斥的关系,其中一个显示时另一个一定是隐藏的。这个场景就非常适合引入状态机,因为它能确保每个时刻只有一种视图是激活的。的确,如果我们想添加一些新视图,比如一个承载设置操作的视图,用状态机来处理这种场景绰绰有余。

一个完整的状态机

 var Events = {
      bind: function(){
        if ( !this.o ) this.o = $({});
        this.o.bind.apply(this.o, arguments);
      },
  trigger: function(){
    if ( !this.o ) this.o = $({});
    this.o.trigger.apply(this.o, arguments);
  }
};

var StateMachine = function(){};
StateMachine.fn  = StateMachine.prototype;
$.extend(StateMachine.fn, Events);

StateMachine.fn.add = function(controller){
  this.bind(&quot;change&quot;, function(e, current){
    if (controller == current)
      controller.activate();
    else
      controller.deactivate();
  });
  
  controller.active = $.proxy(function(){
    this.trigger(&quot;change&quot;, controller);
  }, this);
};

var con1 = {
  activate: function(){ 
    console.log(&quot;controller 1 activated&quot;);
  },
  deactivate: function(){ 
    console.log(&quot;controller 1 deactivated&quot;);
  }
};

var con2 = {
  activate: function(){ 
    console.log(&quot;controller 2 activated&quot;);
  },
  deactivate: function(){ 
    console.log(&quot;controller 2 deactivated&quot;);
  }
};
 
var con3= {
  activate: function(){ 
    console.log(&quot;controller 3 activated&quot;);
  },
  deactivate: function(){ 
    console.log(&quot;controller 3 deactivated&quot;);
  }
};

var sm = new StateMachine;
sm.add(con1);
sm.add(con2);
sm.add(con3);

con2.active();

See the Pen ljyLq by buer (@buer) on CodePen.

URL 中的hash

定位当前页面所用的URL(基于URL)是不能更改的,如果改变则会引起页面的刷新,这是我们要避免发生的。幸好我们有一些解决办法。操作URL 的一种传统办法是改变它的hash。hash 不会发送给服务器,因此更改hash 不会造成页面的刷新。比如,这个
URL 是Twitter 的页面,它的hash 值就是#!/maccman:

http://twitter.com/#!/maccman

可以通过location 对象来读取或修改页面hash :

// 设置hash
window.location.hash = "foo";
assertEqual( window.location.hash , "#foo" );
// 去掉“#”
var hashValue = window.location.hash.slice(1);
assertEqual( hashValue, "foo" );

如果URL 中没有hash,location.hash 则返回空字符串。否则,location.hash 和URL的hash 部分相等,带有# 前缀。
太过频繁地设置hash 也会影响性能,特别是在移动终端里的浏览器中。因此,如果你对hash 的改动太频繁,就要注意限制这种改动,否则在移动终端里可能会造成页面的频繁滚动。

检测hash 的变化

以往检测hash 变化的方法是通过轮询的计时器来监听,这种方法非常原始。现在情形有所改观,现代浏览器都支持hashchange 事件。这是一个window 的事件,如果想检测hash 的改变就需要绑定这个监听:

window.addEventListener("hashchange", function(){ /* ... */ }, false);

使用jQuery 的代码:

$(window).bind("hashchange", function(event){
// hash 发生改变,更改状态
});

当触发hashchange 事件时,我们需要确定应用的当前状态。这个事件的浏览器兼容性非
常不错,主流浏览器的最新版本都支持这个事件:

  • IE >= 8。
  • Firefox >= 3.6。
  • Chrome。
  • Safari >= 5。
  • Opera >= 10.6。

标签: none

添加新评论