seajs实用教程(二) 使用
2015-11-23 15:32:05

120
获取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就完成了。