分类目录归档:SeaJs专题

这个专栏是研究SeaJs应用以及实际记录学习

Seajs配置模式清单

seajs 支持一些配置选项,能让开发、调试更方便。

seajs.config seajs.config(options)

可以使用 config 方法来配置加载器。

seajs.config({
  alias: {
    'es5-safe': 'gallery/es5-safe/0.9.3/es5-safe',
    'json': 'gallery/json/1.0.2/json',
    'jquery': 'gallery/jquery/1.8.2/jquery'
  },
  preload: [
    Function.prototype.bind ? '' : 'es5-safe',
    this.JSON ? '' : 'json'
  ],
  debug: true,
  map: [
    ['http://example.com/js/app/', 'http://localhost/js/app/']
  ],
  base: 'http://example.com/path/to/base/',
  charset: 'utf-8'
});

支持以下配置选项:

alias

当模块标识很长时,可以使用 alias 配置来简化。

seajs.config({
  alias: {
    'app': 'http://path/to/app',
    'jquery': 'gallery/jquery/1.8.2/jquery'
  }
});

a.js:

define(function(require, exports, module) {
    var $ = require('jquery');
      //=> 加载的是 http://path/to/base/gallery/jquery/1.8.2/jquery.js

    var biz = require('app/biz');
      //=> 加载的是 http://path/to/app/biz.js
});

解析某个模块标识时,如果需要临时禁用别名解析,可以在标识前添加一个井号(#):

define(function(require, exports, module) {
    var $ = require('#jquery');
      //=> http://path/to/base/jquery.js
});

alias 最常用来做版本配置与管理,也可以用来做命名空间管理。

preload

使用 preload 配置项,可以在普通模块加载前,提前加载并初始化好指定模块。

// 在老浏览器中,提前加载好 ES5 和 json 模块:
seajs.config({
  preload: [
    Function.prototype.bind ? '' : 'es5-safe',
    this.JSON ? '' : 'json'
  ]
});

preload 中的空字符串会被忽略掉。

注意preload 中的配置,需要等到 use 时才加载。比如:

seajs.config({
  preload: 'a'
});

// 在加载 b 之前,会确保 a 模块已经加载并执行好。
seajs.use('./b');

preload 配置不能放在模块文件里面:

b.js:

seajs.config({
  preload: 'a'
});

define(function(require, exports) {
  // 此处执行时,无法保证 a 模块已经加载并执行好。
});

debug

值为 true 时,加载器会使用 console.log 输出所有错误和调试信息。 默认为 false, 只输出关键信息。

另外,还可以将 debug 值设为 2 . 这种情况下, 每个脚本请求都会加上唯一时间戳。这在测试期间很有用,可以强制浏览器每次都请求最新版本,免去 Ctrl + F5 之烦恼。

map

该配置可将某个文件映射到另一个。可用于在线调试,非常方便。更多信息,请参考 调试实践

base

SeaJS 在解析顶级标识时,会相对 base 路径来解析。详情请参阅 模块标识

注意:一般请不要配置 base 路径,保持默认往往最方便。

charset

获取模块文件时,<script> 或 <link> 标签的 charset 属性。 默认是 utf-8 。

charset 配置项还支持 function 类型:

seajs.config({

  charset: function(url) {
    // xxx 目录下的文件用 gbk 编码加载
    if (url.indexOf('http://example.com/js/xxx') === 0) {
      return 'gbk';
    }
    // 其他文件用 utf-8 编码
    return 'utf-8';
  }

});

本文来源:https://github.com/seajs/seajs/issues/262

Seajs引入jQuery模块介绍与 jQuery 插件的深度模块化(二)实践学习

jQuery 插件非常丰富。在传统使用方式里,比如 jquery-fancybox, 我们需要在页面中显式引入:

1 <script src="/js/jquery.min.js"></script>
2 <script src="/js/jquery.easing-1.3.pack.js"></script>
3 <script src="/js/jquery.mousewheel-3.0.4.pack.js"></script>
4 <script src="/js/jquery.fancybox-1.3.4.js"></script>
5 <link rel="stylesheet" href="/js/jquery.fancybox-1.3.4.css"/>

fancybox 依赖 easing 和 mousewheel, 因此还引入了这两个插件文件。当使用的插件越多,依赖关系越复杂时,上面的 script 列表会越难维护。

 

模块化

我们看下在 seajs 里如何做。

看 demo: FancyBox 1.3.4 | Demonstration

页面源码里,引入的 script 只剩下一处:

1 <script src="http://seajs.org/dist/sea.js" data-main="./init"></script>

来看 init.js:

1 /* init.js */
2 define(function(require) {
3   var $ = require('jquery');
4   require('./fancybox/jquery.fancybox-1.3.4.js')($);
5
6   // snip...
7 });

init 依赖 jquery 和 fancybox, 因此 require 了这两个模块。

 

require('./fancybox/jquery.fancybox-1.3.4.js') 返回的是 function, 其代码为:

1 /* jquery-fancybox-1.3.4.js */
2 define(function(require) { return function($) {
3   require('./jquery.easing.1.3.js')($);
4   require('./jquery.mousewheel.js')($);
5   require('./jquery.fancybox-1.3.4.css');
6   
7   // snip...
8 }});

 

jQuery 插件的机制是往 $ 或 $.fn 上添加插件成员。在 seajs 里,我们可以将插件包装成一个函数。这样,在使用时,传入需要被扩展的 jQuery 实例就好。

对于不依赖其它插件的 jquery 插件,模块化包装就更简单了:

1 /* jquery.mousewheel.js */
2 define(function() { return function($) {
3   // snip..
4 }});

 

通过这种方式,我们可以将任何 jquery 插件封装成 seajs 的模块!

共享模式

上面的方式可以概括为:

1 var $ = require('jquery');
2 require('some-jquery-plugin')($);

 

这是一种共享模式,一旦添加某个插件功能到 jQuery 后,其他模块里,require('jquery') 返回的 jquery 对象会自动拥有已经添加过的插件功能。这种共享模式可以对 jquery 的插件进行集中管理。比如对于具体项目,可以在 init.js 文件里:

1 /* init.js */
2 define(function(require) {
3   var $ = require('jquery');
4   require('jq-plugin-a')($);
5   require('jq-plugin-b')($);
6   require('jq-plugin-c')($);
7   
8   // snip...
9 });

 

这样,在其它模块中,通过 require('jquery') 就可以得到添加过所需要的所有插件功能的 jquery 对象。

子类模式

当项目很复杂,jquery 插件之间有可能存在冲突。为了尽可能避免冲突,我们可以通过 jQuery 子类来解决:

1 var $ = require('jquery').sub();
2 require('some-jquery-plugin')($);

 

通过 sub 方法,每个依赖 jquery 的模块,都可以得到 jQuery 的一个子类。插件往这个子类添加成员,因此各个模块之间的 jquery 是独立的了,彼此不会影响。

延续习惯

无论是共享模式还是子类模式,都不会破坏插件本身的使用方式,原来是怎么调用的,现在依旧怎么用。唯一不同的是引入方式。模块化之前,我们需要手动引入 script 标签来解决;模块化之后,我们通过 require 来管理加载和依赖。

小结

通过这种包装,我们就可以在 seajs 里使用任何 jquery 插件了,并且通过 seajs 的 node 模块,还可以让 jquery 的插件在 node 环境中运行。

一切就这么简单。看起来没什么变化,但变化就在其中!

 

转载来源:http://lifesinger.wordpress.com/2011/05/18/jquery-plugins-modulization/

Seajs引入jQuery模块介绍与 jQuery 插件的深度模块化(一)入门学习

jQuery 模块

大名鼎鼎的 jQuery 就不多介绍了,详细介绍推荐官网:jquery.com

几点感悟:

  1. jQuery 是 DOM 操作类库,其核心功能是找到 DOM 元素并对其进行操作。
  2. 拿 jQuery 与 YUI, Dojo 等框架相比是不公平的,就如拿轮胎和汽车相比一样。jQuery 只是一个轮胎,功能很单一,YUI 和 Dojo 等则是相对完整的汽车,除了轮胎,还有引擎、外壳等等。
  3. 说 jQuery 不适合构建大型应用,就如说轮胎不适合参加赛车比赛一样不合逻辑。你可以用 jQuery 做轮胎,然后选择其他部件组合起来去 DIY 一辆赛车。能否胜出,得看赛车手的 DIY 水准。
  4. jQuery 的困局在于 DIY 高手不多,经常是一个好轮胎挂上一堆破破烂烂的外壳就上前线了。jQuery 的破局也在于 DIY. DIY 意味着灵活、可替换性,意味着可快速前行和高性能。
  5. jQuery 灵活性带来的缺陷,比如有可能由选择器和链式风格导致的低效 DOM 操作,目前在提供了同类功能的 YUI3 等类库中同样存在。这不是类库的问题,更多是因为使用者的经验欠缺导致的。就如一把优秀的菜刀,到了一个拙劣的厨子手中,依旧切不好菜一样。工具很重要,但更重要的是我们得提升自己的刀工。
  6. 最后,回到第一点:jQuery 是 DOM 操作类库。非 DOM 操作,都是 jQuery 的辅助功能,不是 jQuery 的强项,就如菜刀不能当斧头用一样。

我们可以通过简单封装,让 jQuery 成为 CommonJS 的模块。这样,调用时只要 require 即可:

test.html:

2 <script>
3 seajs.use('./init');
4 </script>

 

init.js:

01 seajs.config({
02   alias: {
03     'juery': 'jquery/1.6.1/jquery'
04   }
05 });
06
07 define(function(require, exports, module) {
08   var $ = require('jquery');
09   // do something with jQuery
10 });

 

jQuery 插件的模块化

jQuery 提供了 DOM 操作功能,在实际应用中,我们还需要 cookie, template, storage 等等一系列功能。这时可以从 jQuery 社区中寻找各种插件来完成。大部分插件通过 jQuery 插件的模块化 一文中提供的方法封装就好。

之前的封装方法,总结成一句话是:“jQuery 穿肠过,插件身上留”。正如 Kidwind 反馈的一样,每次“穿肠过”的时候都要运行一次插件代码,频繁调用某些插件时,会存在 CPU 浪费,还可能带来隐患:

假设有以下jquery插件a, b, c, d,它们之间的关系如下
b 依赖于 a
c 依赖于 a
d 依赖于 b c

假设页面使用到d插件,那么插件a将进行两次初始化,也就是会调用两次
var $ = require(‘jquery’);
require(‘a’)($);
进行插件a的注册,当系统复杂时,重复的插件注册会不会影响系统的性能,同时会不会存在隐患?如插件b对引用的插件a进行了部分功能扩展,当引入插件c的时候又重新注册了插件a,那么插件b对插件a的扩展将不存在了,当然改写插件功能的实际情况也许不会存在,此处只是举个例子,说明隐患的存在。
如何避免重复的插件注册,可以避免隐患,同时获得更好的性能(避免了多次插件注册的运算耗时)。

面对这种情况,我们究竟应该如何做好 jQuery 插件的模块化?

jQuery 插件的形式

jQuery 插件一般可以总结为以下模板

01 (function($) { 
02   // Main plugin function
03   $.fn.PLUGIN = function(options) {
04     // snip...
05   };
06
07   // Public plugin function
08   $.fn.PLUGIN.FUNCT = function() {
09     // Cool JS action
10   };
11
12   // Default settings for the plugin
13   $.fn.PLUGIN.defaults = { /* snip... */ };
14
15   // Private function that is used within the plugin
16   // snip...
17 })(jQuery);

 

简言之就是往 $.fn 上添加新成员,有部分插件还会往 $ 上添加成员。

之前的“穿肠过”模块化方式,可以表示为:

1 define(function() { return function($) {
2   $.fn.PLUGIN = ...
3 }});

 

调用方式:

1 define(function(require, exports) {
2   var $ = require('jquery');
3   require('some-jquery-plugin')($);
4
5   $(sth).PLUGIN(...);
6 });

 

不是很直观,不够方便,还有前面提到的隐患。

深度模块化

为了更好的模块化,意味着我们要添加更多代码:

some-jquery-plugin.js:

01 define(function(require, exports, module) {
02   var $ = require('jquery').sub();
03
04   // Main plugin function
05   $.fn.PLUGIN = function(options) {
06     // snip...
07   };
08
09   // Public plugin function
10   $.fn.PLUGIN.FUNCT = function() {
11     // Cool JS action
12   };
13
14   // Default settings for the plugin
15   $.fn.PLUGIN.defaults = { /* snip... */ };
16
17   // Private function that is used within the plugin
18   // snip...
19
20   module.exports = $;
21 });

 

这样封装后,调用变成:

1 define(function(require, exports) {
2   var $ = require('jquery');
3   var PLUGIN = require('some-jquery-plugin');
4
5   PLUGIN(sth).PLUGIN(...);
6 });

 

这样能解决之前提到的重复初始化问题,但是 PLUGIN(sth).PLUGIN(...) 的使用方式怪怪的。比如这个非常帅的 chosen插件,按照上面的方式模块化后,调用方式为:

1 chosen('#some-id').chosen();

 

虽然可用,但怎么看怎么别扭。这是因为 jQuery 是以 DOM 为中心的,代码的默认流程是找到要操作的 DOM 元素,然后对其进行操作。这种代码书写方式,对于模块后的插件来说,很别扭。更好的期待中的调用方式是:

1 define(function(require, exports) {
2   var $ = require('jquery');
3   var Chosen = require('chosen');
4
5   var chosen = new Chosen(selector, options);
6   chosen.doSth(...);
7 });

 

理论上,我们甚至可以不知道 chosen 依赖 jQuery, 我们需要关心的只是 chosen 的 API. 上面这种理想的调用方式,需要我们对插件进行“深度”模块化:

some-jquery-plugin.js:

01 define(function(require, exports, module) {
02   var $ = require('jquery');
03
04   // Main plugin function
05   function PLUGIN(selector, options) {
06     var els = $(selector);
07     // snip...
08   };
09
10   // Public plugin function
11   PLUGIN.FUNCT = function() {
12     // Cool JS action
13   };
14
15   // Default settings for the plugin
16   PLUGIN.defaults = { /* snip... */ };
17
18   // Private function that is used within the plugin
19   // snip...
20
21   module.exports = PLUGIN;
22 });

 

也就是说,在 plugin 的代码里,我们并不对 $.fn 或 $ 进行扩展,只用 $ 来进行 DOM 操作而已,返回的是独立的 PLUGIN 对象,就和我们写普通的业务模块一样。这样,就实现预期中更优雅的调用方式。

jQuery 的插件机制,在模块化面前很鸡肋。jQuery 一直被冠以“不适合大型项目”,也和 jQuery 的这种插件机制有关系。这会导致大家都去污染 $.fn, 这就和污染全局变量一样。项目一大,冲突的概率,和调试的成本都会变大,很悲剧。

因此,推荐大家利用模块的机制去重构一部分好用的 jQuery 插件,目前 dew 项目里已经重新实现了 cookie 等部分模块。强烈推荐大家都参与进来,将自己喜欢的,常用的 jQuery 等插件迁移过来。或者推进插件作者直接修改源码,增加对 CommonJS 的支持。路漫漫,但众人拾柴火焰高,星火可燎原,期待大家的参与。

建议大家直接 fork dew 项目,可以将自己重构的模块 pull request 过来,邮件给 seajs(at)googlegroups.com 群组。讨论和 code review 后,就可以转成 dew 的正式模块。

等模块丰富起来,我们就可以有更多时间去做更意思的事情了。

转载来源:http://lifesinger.wordpress.com/2011/08/19/jquery-introduction-and-plugins-modulization/

SeaJS参数手册chm版本号是1.2

最近公司要JS模块化,或者是我自己决定要求这样,毕竟这些新的概念和技术与稳定性是不用去质疑的,那么现在有这么多的大公司都在提倡开发,小子我也不必去分析那些,直接就拿来用吧!

 

为什么使用 SeaJS ?

SeaJS 追求简单、自然的代码书写和组织方式。使用 SeaJS,可以让我们的工作更轻松愉快:

  • 简单易用:提供与 Node.js 一般的模块化开发体验。
  • 体系化:通过包管理工具 spm 提供预编译、打包部署等功能。

SeaJS 的最大好处是:提升代码的可维护性。如果一个网站的 JS 文件超过 3 个,就适合用 SeaJS 来组织和维护代码。开发的代码越多,SeaJS 就越适合。SeaJS 还提供常用插件,对开发和调试友好,并具有丰富的可扩展接口。

SeaJS 期望让程序员享受编码的乐趣,让生活更美好。

手册下载:http://pan.baidu.com/share/link?shareid=143582&uk=2870623498