获取seajs
seajs版本的git地址 https://github.com/seajs/seajs
使用git clone方法获取seajs
git clone https://github.com/seajs/seajs/archive/master.zip
2 官网推荐使用 spm 安装:
$ npm install spm -g $ spm install seajs
学习资料:
在开始说明seajs之前先了解下seajs开发的原理。
使用SeaJS开发JavaScript的基本原则就是:一切皆为模块。引入SeaJS后,编写JavaScript代码就变成了编写一个又一个模块,SeaJS中模块的概念有点类似于面向对象中的类——模块可以拥有数据和方法,数据和方法可以定义为公共或私有,公共数据和方法可以供别的模块调用。
另外,每个模块应该都定义在一个单独js文件中,即一个对应一个模块。
seajs常用API概览
seajs.config 用来对 Sea.js 进行配置。
seajs.use 用来在页面中加载一个或多个模块。
define 用来定义模块。
require 用来获取指定模块的接口。
require.async 用来在模块内部异步加载一个或多个模块。
exports 用来在模块内部对外提供接口。
module.exports 用来在模块内部对外提供接口。
下来我们从一个开发者角度来对这些API进行下深入的了解。开发个系统当然,先需要做下系统的配置,我们就了解下seajs.config。
seajs.config是对全局进行配置,配置项有下面几种。
base 在解析顶级标识时,会相对 base 路径来解析。
alias 当模块标识很长时,可以使用 alias 来简化。
charset 表示下载js时script标签的charset属性。
timeout 表示下载文件的最大时长,以毫秒为单位。
debug 表示是否工作在调试模式下。
这几个都是很常用的配置,举个例子如下
seajs.config({
base: 'path/to/js/',
alias: {
'jquery': 'jquery.js'
},
charset: 'utf-8',
timeout: 20000,
debug: false
});
base用来指定根路径,可以指定为绝对路径或者相对路径。
譬如使用require("a") 则加载文件path/to/js/a.js
debug:可用的有三种 0|1|2
0: 普通状态,你就当啥也没发生
1: 调试状态,combo 会失效,其他和 0 的时候一样
2: 无缓存状态,所有请求都会自动在后面添加一个时间戳,其他和 1 的时候一样
比较不常用的:
map方法,可将某个文件映射到另一个。可用于在线调试,非常方便。
map: [
['.css', '.css?v=' + version],
['.js', '.js?v=' + version]
]
preload方法
了解完了配置文件,就说下怎么用呢,怎么加载,从哪入口呢?
seajs.use()方法
seajs.use主要用于载入入口模块。入口模块相当于C程序的main函数,同时也是整个模块依赖树的根。
seajs.use用法如下:
//单一模式
seajs.use('./a');
//回调模式
seajs.use('./a', function(a) {
a.run();
});
//多模块模式
seajs.use(['./a', './b'], function(a, b) {
a.run();
b.run();
});
一般seajs.use只用在页面载入入口模块,SeaJS会顺着入口模块解析所有依赖模块并将它们加载。如果入口模块只有一个,也可以通过给引入sea.js的script标签加入”data-main”属性来省略seajs.use,如下所示。
<script src="./sea.js" data-main="./init"></script>
这种写法会令html更加简洁。
了解了这么多,下来就到了重点了,模块编写。
define(factory) 是一个全局函数,用来定义模块。 接收一个参数 可以使对象 可以使字符串 也可以使函数 接收两个以上参数 define(id?, deps?, factory) 字符串 id 表示模块标识,数组 deps 是模块依赖。 如果只有一个参数,则赋值给factory。 如果有两个参数,第二个赋值给factory;第一个如果是array则赋值给deps,否则赋值给id。 如果有三个参数,则分别赋值给id,deps和factory。
主要来看工厂函数factory解析
工厂函数是模块的主体和重点。在只传递一个参数给define时(推荐写法),这个参数就是工厂函数,此时工厂函数的三个参数分别是:
require——模块加载函数,用于记载依赖模块。 require加载机制,在后面做统一讨论。 exports——接口点,将数据或方法定义在其上则将其暴露给外部调用。 module——模块的元数据。 这三个参数可以根据需要选择是否需要显示指定。 exports: exports 是一个对象,用来向外提供模块接口。 除了给 exports 对象增加成员,还可以使用 return 直接向外提供接口。 seajs对外提供 module是一个对象,存储了模块的元信息,具体如下: module.id——模块的ID。 module.dependencies——一个数组,存储了此模块依赖的所有模块的ID列表。 module.exports——与exports指向同一个对象。
require.async
SeaJS会在html页面打开时通过静态分析一次性记载所有需要的js文件,如果想要某个js文件在用到时才下载,可以使用require.async:
require.async('/path/to/module/file', function(m) {
//code of callback...
});
这样只有在用到这个模块时,对应的js文件才会被下载,也就实现了JavaScript代码的按需加载。
seajs的API就先讲到这里,对于使用,大家可以看下课时的应用示例。
编写自己的seajs模块
模块编写:
SeaJS中使用define函数定义一个模块。
factory可以接收三个参数:require, exports, module。
require——模块加载函数,用于记载依赖模块。 exports——接口点,将数据或方法定义在其上则将其暴露给外部调用。 module——模块的元数据。
exports 仅仅是 module.exports 的一个引用。在factory内部给exports重新赋值并不会改变module.exports的值。因此给exports赋值是无效的,不能用来更改模块接口。
对 module.exports 的赋值需要同步执行,不能放在回调函数里。
require 是一个方法,接受 模块标识 作为唯一参数
使用模块系统内部的路径解析机制来解析并返回模块路径。该函数不会加载模块,只返回解析后的绝对路径。
require书写约定
- 正确拼写 模块 factory 构造方法的第一个参数 必须 命名为 require 。
- 不要修改 不要重命名 require 函数,或在任何作用域中给 require 重新赋值。
- 使用直接量 require 的参数值 必须 是字符串直接量。
可以把require看成语法关键字就行。
关于模块的几种写法:
第一种是教科书式的写法,也是最常用的一种写法。
define(function(require, exports, module) {
var a = require('a'); //引入a模块
var b = require('b'); //引入b模块
var data1 = 1; //私有数据
var func1 = function() { //私有方法
return a.run(data1);
}
exports.data2 = 2; //公共数据
exports.func2 = function() { //公共方法
return 'hello';
}
});
第二种方法是抛弃exports和module的方式:
define(function(require) {
var a = require('a'); //引入a模块
var b = require('b'); //引入b模块
var data1 = 1; //私有数据
var func1 = function() { //私有方法
return a.run(data1);
}
return {
data2: 2,
func2: function() {
return 'hello';
}
};
});
第三种方式:类似于JSON写法,其实就是没有方法的一种写法。
define({
data: 1,
func: function() {
return 'hello';
}
})
现在你也可以来编写自己的cmd模块了,下节我们继续了解怎么改造现有文件成为cmd模块。
如何改造现有文件为 CMD 模块
改造主流模块
这里指的是 jQuery、Moment、Backbone、underscore 等业界主流模块,这些模块一般都有对 AMD 和 CommonJS 的支持代码,例如 jQuery 源文件的最后几行:
if ( typeof module === "object" && module && typeof module.exports === "object" ) {
module.exports = jQuery;
} else {
window.jQuery = window.$ = jQuery;
if ( typeof define === "function" && define.amd ) {
define( "jquery", [], function () { return jQuery; } );
}
}
还有 Backbone 里:
var Backbone;
if (typeof exports !== 'undefined') {
Backbone = exports;
} else {
Backbone = root.Backbone = {};
}
对于有这些兼容代码的,只需要去掉 define.amd 的支持,或是直接包装上 define 就可以了。
define(function(require, exports, module) {
// code here ...
});
如果没有模块化的兼容代码,有时候需要手动引入依赖,以及暴露对应的接口。
define(function(require, exports, module) {
var $ = require('$');
// code here ...
module.exports = Placeholders;
});
可以参考 cmdjs/gallery 里的 Gruntfile 对这些主流模块的代码替换方式。
现有的 JS 文件
对于一些现有的普通 JS 文件,相对简单的多,参考 CMD 的书写规范,把那些暴露到全局命名空间的接口用 CMD 的方式进行改造就可以了。
比如现有文件 util.js 。
window.util = window.util || {};
util.addClass = function() {
window.css();
};
util.queryString = function() {};
改为:
define(function(require, exports, module) {
// 引入依赖
var css = require('css');
util.addClass = function() {
css();
};
util.queryString = function() {};
// 暴露对应接口
module.exports = util;
});
这里不啰嗦,就是基本的 CMD 书写规范。实际的改造过程中,情况可以比上面的例子要复杂一些,具体情况具体解决就行。
改造 jQuery 插件
这也是一个经常遇到的问题,我们推荐以下的包装方式:
// jquery-plugin-abc
define(function(require, exports, module) {
var $ = require('$');
// 插件的代码
$.fn.abc = function() {};
});
这样的改造方式实际上是强化了原有的 $ 对象(而不是重新包装出一个新的 $),在实际调用时,可以用下面的方式:
seajs.use(['$', 'jquery-plugin-abc'], function($) {
// $ 有了 jquery-plugin-abc 所提供的插件功能
$.abc();
});
更多 jQuery 插件的包装,可以参考 cmdjs/jquery 里的做法。
实在是无法写出比官网还好的教程了,摘自https://github.com/seajs/seajs/issues/971
整体的目录结构:
├── index.html
├── js
│ ├── a.js
│ ├── b.js
│ └── c.js
└── lib
└── seajs
└── seajs1.3.0.js
其中
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>seajs demo</title>
</head>
<body>
<script src="lib/seajs/seajs1.3.0.js"></script>
<script>
seajs.config({
base:"./js/"
})
seajs.use(["a"],function(){
console.log("a.js and b.js saved");
})
</script>
</body>
</html>
a.js:
define(function(require,exports,module){
var b = require("b");
console.log("a.js exec");
console.log(module);
})
b.js:
define(function(require,exports,module){
var b = require("a");
console.log("b.js exec");
console.log(module);
var c = require.async("c");
})
c.js:
define(function(require,exports,module){
console.log("c.js exec");
console.log(module);
})
一个简单的demo就完成了。

