模块化工具seajs
2017-02-26 17:33
218 查看
一、引入sea.js
<script type="text/javascript" src="js/sea.js"></script>
二、如何变成模块
用define(function(require,exports,module){})包裹
require模块之间依赖的接口
exports对外提供方法的接口
define(function(require,exports,module) { function show(){ alert('aa'); } });
三、如何调用模块
exports用来对外提供接口
define(function(require,exports,module) { function show(){ alert('aa'); } exports.show=show; });
四、如何使用模块
seajs.use('module.js',function(ex){}),第一个参数是请求模块的路径
module.js,第二个参数是请求完成后的回调函数
function(ex){},回调用有一个参数
ex,这个参数就是模块提供的接口
默认根目录是
sea.js所在的目录。如果想以当前页面所在的目录为根目录,那么只需要加上相对表示符
./
seajs.use('module.js'); seajs.use('./js/module.js',function(ex){ ex.show(); });
五、如何依赖模块(模块与模块之间进行调用,即依赖关系)
通过require('./module3.js')来加载依赖
①如果module3.js是一个普通的模块(即没有用
define(function(require,exports,module){})包裹起来的模块)可以通过如下方式来加载依赖:
define(function(require,exports,module) { require('./module3.js'); function show(){ alert(a) } exports.show=show; });
②如果
module3.js是一个标准的seajs模块(即用
define(function(require,exports,module){})包裹起来的模块),当引入依赖依赖模块时,
require执行完的结果就是
exports
在控制台打印
require('./module3.js'),会发现打印出来的是一个对象
Object {a: 100},所以要通过如下方式来加载依赖:
define(function(require,exports,module) { var a=require('./module3.js').a; function show(){ alert(a) } exports.show=show; });
六、Grunt构建(grunt-cmd-contact grunt-cmd-transport contrib-clean)
如果用构建工具合并所有的模块之后,模块将会多两个参数id、
[]。分别表示,当前模块的
标识(id)和当前模块的
依赖([])。注意:依赖是一个数组可以为空
define(id,['',''],function(require,exports,module){})
七、use如何引用多个模块
如果想用use同时引入多个模块,就需要用一个数组来存放依赖,如下。回调函数中的参数,分别代表对应的模块提供的方法即
ex1==module.exports==exports
seajs.use(['./js/module1.js','./js/module2.js'],function(ex1,ex2) { ex1.show(); ex2.show(); });
八、sea加ID有利于提取
<script type="text/javascript" src="js/sea.js"></script>
九、如何改造文件为CMD模块
用define(function(require,exports,module){})包裹,并输出相应的接口
exports.show=show;
十、调试接口cache
seajs.cache
十一、模块标识
模块标识是一个字符串,用来标识模块。在 require、 require.async 等加载函数中,第一个参数都是模块标识。1、一个模块标识由斜线
(/)分隔的多项组成。
2、每一项必须是小驼峰字符串、
.或
..。
3、模块标识可以不包含文件后缀名,比如
.js。
4、模块标识可以是 相对 或 顶级 标识。如果第一项是
.或
..,则该模块标识是相对标识。
5、顶级标识根据模块系统的基础路径来解析。
6、相对标识相对
require所在模块的路径来解析。
注意,符合上述规范的标识肯定是
Sea.js的模块标识,但
Sea.js能识别的模块标识不需要完全符合以上规范。 比如,除了大小写字母组成的小驼峰字符串,
Sea.js的模块标识字符串还可以包含下划线
_和连字符
-, 甚至可以是
http://、
https://、
file:///等协议开头的绝对路径。
11.1相对标识
相对标识以.开头,只出现在模块环境中(d
efine的
factory方法里面)。相对标识永远相对当前模块的
URI来解析
// 在 http://example.com/js/a.js 的 factory 中: require.resolve('./b'); // => http://example.com/js/b.js // 在 http://example.com/js/a.js 的 factory 中: require.resolve('../c'); // => http://example.com/c.js
11.2顶级标识
顶级标识不以点(.)或斜线(/)开始, 会相对模块系统的基础路径(即Sea.js的
base路径)来解析
1、假设
base路径是:
http://example.com/assets/
在模块代码里:
require.resolve('gallery/jquery/1.9.1/jquery'); // => http://example.com/assets/gallery/jquery/1.9.1/jquery.js
2、模块系统的基础路径即 base 的默认值,与 sea.js 的访问路径相关
如果 sea.js 的访问路径是
http://example.com/assets/sea.js则 base 路径为
http://example.com/assets/
3、当 sea.js 的访问路径中含有版本号时,base 不会包含 seajs/x.y.z 字串。 当 sea.js 有多个版本时,这样会很方便。
如果 sea.js 的路径是:
http://example.com/assets/seajs/1.0.0/sea.js则 base 路径是:
http://example.com/assets/
4、当然,也可以手工配置 base 路径:
seajs.config({ base: 'http://code.jquery.com/' }); // 在模块代码里: require.resolve('jquery'); // => http://code.jquery.com/jquery.js
11.3普通路径
除了相对和顶级标识之外的标识都是普通路径。普通路径的解析规则,和 HTML 代码中的<script src="..."></script>一样,会相对当前页面解析。
假设当前页面是
http://example.com/path/to/page/index.html
// 绝对路径是普通路径: require.resolve('http://cdn.com/js/a'); // => http://cdn.com/js/a.js // 根路径是普通路径: require.resolve('/js/b'); // => http://example.com/js/b.js // use 中的相对路径始终是普通路径: seajs.use('./c'); // => 加载的是 http://example.com/path/to/page/c.js seajs.use('../d'); // => 加载的是 http://example.com/path/to/d.js
提示:
1、顶级标识始终相对 base 基础路径解析。
2、绝对路径和根路径始终相对当前页面解析。
3、require 和 require.async 中的相对路径相对当前模块路径来解析。
4、seajs.use 中的相对路径始终相对当前页面来解析。
十二、如何改造现有文件为 CMD 模块
12.1、改造主流模块这里指的是 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; });
12.2、现有的 JS 文件
对于一些现有的普通 JS 文件,相对简单的多,参考 CMD 的书写规范,把那些暴露到全局命名空间的接口用 CMD 的方式进行改造就可以了。12.3、改造 jQuery 插件
我们推荐以下的包装方式:// jquery-plugin-abc define(function(require, exports, module) { var $ = require('$'); // 插件的代码 $.fn.abc = function() {}; });
这样的改造方式实际上是强化了原有的 对象(而不是重新包装出一个新的),在实际调用时,可以用下面的方式:
seajs.use(['$', 'jquery-plugin-abc'], function($) { // $ 有了 jquery-plugin-abc 所提供的插件功能 $.abc(); });
十三、配置
seajs.config(options)
seajs.config({ // 别名配置(当模块标识很长时,可以使用 alias 来简化) alias: { 'es5-safe': 'gallery/es5-safe/0.9.3/es5-safe', 'json': 'gallery/json/1.0.2/json', 'jquery': 'jquery/jquery/1.10.1/jquery' }, // 路径配置(当目录比较深,或需要跨目录调用模块时,可以使用 paths 来简化书写) paths: { 'gallery': 'https://a.alipayobjects.com/gallery' }, // 变量配置(有些场景下,模块路径在运行时才能确定,这时可以使用 vars 变量来配置) vars: { 'locale': 'zh-cn' }, // 映射配置(该配置可对模块路径进行映射修改,可用于路径转换、在线调试等) map: [ ['http://example.com/js/app/', 'http://localhost/js/app/'] ], // 预加载项(使用 preload 配置项,可以在普通模块加载前,提前加载并初始化好指定模块) preload: [ Function.prototype.bind ? '' : 'es5-safe', this.JSON ? '' : 'json' ], // 调试模式(值为 true 时,加载器不会删除动态插入的 script 标签。插件也可以根据 debug 配置,来决策 log 等信息的输出。) debug: true, // Sea.js 的基础路径(Sea.js 在解析顶级标识时,会相对 base 路径来解析) base: 'http://example.com/path/to/base/', // 文件编码(获取模块文件时,<script> 或 <link> 标签的 charset 属性。 默认是 utf-8) charset: 'utf-8' });
十四、ID 和路径匹配原则 (注意:这是一个万人坑)
所谓 ID 和路径匹配原则 是指,使用seajs.use或
require进行引用的文件,如果是具名模块(即定义了
ID的模块),会把
ID和
seajs.use的路径名进行匹配,如果一致,则正确执行模块返回结果。反之,则返回
null。例如:
seajs.use('lib/jquery', function($) { // use $ });
或者在模块中 require :
define(function(require, exports, module) { var $ = require('lib/jquery'); // use $ });
当
jQuery文件是下面的情况时,上述的变量
$能拿到正确的返回结果。
// 文件路径是 lib/jquery.js // ID 和实际路径匹配了(.js 后缀会自动补上) define('lib/jquery', function(require, exports, module) { // jquery code });
下面的代码则返回 null:
// 文件路径是 lib/jquery.js // 但是 ID 是 lib/jquery.min.js // ID 和路径不匹配 define('lib/jquery.min', function(require, exports, module) { // jquery code });
而匿名模块始终能正确返回结果:
// lib/jquery.js // 匿名模块,不需要进行匹配 // 但是文件中只能有一个 define 块 define(function(require, exports, module) { // jquery code });
注意这里用于匹配的
ID都是经过
alias和
path解析并且补完后缀之后的。为什么要有这个原则
首先,
Sea.js的模块启动接口秉承的是路径即
ID的设计原则。
seajs.use的方法的第一个参数被规定为文件路径(而不是
ID),这样的设计减轻了记忆模块
ID的负担,无论是匿名模块还是具名模块,开发者只需要知道文件放在哪儿就行了。
进一步的,之所以有这个
ID和路径匹配原则,是因为在
CMD的书写规范中,一个文件对应一个模块,所有的模块都是匿名模块(即
define(factory)的形式)。那么当
seajs.use某模块时,这个模块对应的文件里的唯一的
define方法理所当然的是这个模块的执行代码,这时可以正确返回结果。
但是在生产环境下,静态文件不可避免地需要进行合并打包或者进行
combo,以优化请求数提高页面性能。这时,一个
js文件可能有很多
define()方法。
define(funtion(require, exports, module) { // module a }); define(funtion(require, exports, module) { // module b }); define(funtion(require, exports, module) { // module c });
那么请问,当
seajs.use这个文件时,应该返回哪个模块?
所以这时候 ID 就派上了用场,我们可以这样写:
// path/a.js define('path/a', funtion(require, exports, module) { // module a }); define('path/b', funtion(require, exports, module) { // module b }); define('path/c', funtion(require, exports, module) { // module c });
我们定义好每个模块的
id,在
Sea.js里,那个和文件路径匹配的
ID的模块就是这个文件的主模块。此时:
seajs.use('path/a', function(a) { // got a, not b or c });
这个原则保证了我们能够自由合并模块来优化性能,
seajs-combo和
spm-build的构建机制都是基于此原则。
在
RequireJS中,也有类似的原则:
http://requirejs.org/docs/errors.html#mismatch更深一步可能有人要问为啥一定要把
ID定为文件路径,
Sea.js不是可以自定义
ID吗,像下面这样:
define('module-id', funtion(require, exports, module) { // module id }); // 然后就可以 seajs.use('module-id', function(Module) { // Module });
上面的代码当然可以运行。但是有一点,任何一个模块的运行都涉及到两个步骤:模块定义 和 模块执行,上面的代码两个步骤都包括在内。而使用了
Sea.js,我们不希望用户去手动写 script 标签引用模块。希望只需要
seajs.use模块的文件路径即可(入口唯一):
seajs.use('path/to/module', function(Module) { // Module });
Sea.js会自动插入
script标签,完成定义步骤,然后执行模块,拿到模块的输出。所以当一个文件里有多个
define时,只能用
ID是否匹配
use中的路径来判断是否主模块。
当然可以回避掉这个原则,你只需要自己负责模块的定义部分,再自己
seajs.use之前定义好的模块
ID就行。
<!-- 各种模块的定义 define define define --> <script src="http://example.com/modules.js"></script> <script> // 这时 use 的第一个参数就可以不必是文件路径了,因为已经有定义好的模块 ID 了 seajs.use('jquery', function($) { // $ }); </script>
或者通过
alias来帮助
ID匹配上最终的路径,这样就和
RequireJS的方案基本一致了。
// lib/jquery-1.7.2.js 的内容如下 define('$', funtion(require, exports, module) { // jQuery });
这样就不需要自己去引用上面的文件,可以直接通过
seajs.use调用。
seajs.config({ alias: { $: 'lib/jquery-1.7.2.js' } }); seajs.use('$', function() { // Got $ ! });
相关文章推荐
- 前端教程&开发模块化/规范化/工程化/优化&工具/调试&值得关注的博客/Git&面试-资源汇总
- 前端模块化之seajs
- 前端模块化开发之seaJs
- 模块化前端编程 seajs使用
- Javascript模块化开发,使用模块化脚本加载工具RequireJS,提高你代码的速度和质量。
- 详解前端模块化工具-webpack
- SeaJS前段模块化编程学习2
- gulp基于seaJs模块化项目打包实践
- angularJs(三)--angularJs的模块化和工具方法
- 使用SeaJS实现模块化JavaScript开发
- webpack 前端资源模块化管理和打包工具
- JS模块化工具requirejs教程02
- JavaScript模块化开发库之SeaJS
- Seajs 简易文档 提供简单、极致的模块化开发体验
- Seajs 基础----使用SeaJS实现模块化JavaScript开发
- 前端构建和模块化工具-coolie
- SeaJS快速入门,让js代码模块化 - 2011-09-09修订,新添参考资料
- SeaJS模块化开发-基础篇
- seajs的使用--主要了解模块化
- JS模块化工具requirejs教程(一):初识requirejs