CMD(
表示通用模块定义,该规范是国内发展出来的,由阿里的玉伯提出。就像 AMD 有个 requireJS,CMD 有个浏览器的实现 SeaJS,SeaJS 和 requireJS 一样,都是 javascript 的模块化解决方案。本文将详细介绍 CMD 和 seaJS
- Common Module Definition)
在 Sea.js 中,所有 JavaScript 模块都遵循 CMD(Common Module Definition)模块定义规范。该规范明确了模块的基本书写格式和基本交互规则
AMD 规范简单到只有一个 API,即 define 函数
- define([module-name?], [array-of-dependencies?], [module-factory-or-object]);
module-name: 模块标识,可以省略
array-of-dependencies: 所依赖的模块,可以省略
module-factory-or-object: 模块的实现,或者一个 JavaScript 对象
CMD 规范也与之类似,只不过第三个参数 factory 的实现方式不同。在 CMD 规范中,一个模块就是一个文件。代码的书写格式如下
- define(id?, deps?, factory)
与 AMD 规范类似,define 是一个全局函数,用来定义模块。字符串
表示模块标识,数组
- id
是模块依赖。这两个参数可以省略,通常由构建工具自动生成
- deps
通常地,define() 方法的第三个参数 factory 是一个函数,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。
方法在执行时,默认会传入三个参数:
- factory
、
- require
和
- exports
- module
[注意]factory() 方法的参数如果不需要,可以省略,但不可以修改,如修改为'a'、'b'、'c',也不可以改变其参数的顺序。在函数内部,也不能对参数名重新赋值,如'var a = require; '
- define(function(require, exports, module) {
- // 模块代码
- });
【require】
是
- require
函数的第一个参数。
- factory
是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口。通俗地说,通过 require() 方法来调用其他模块的属性或方法
- require
- define(function(require, exports, module) {
- // 获取模块 a 的接口
- vara = require('./a');
- // 调用模块 a 的方法
- a.doSomething();
- });
这个 require() 方法的实现和功能都特别类似于 CommonJS 中的 require() 方法。或许,有人会有疑惑,require() 不是一个同步方法吗?在 CommonJS 中是的,在 seaJS 中也可以这么说,但并不完整。更合理的说法应该是,模块内的同步加载,实际表现为对模块 a 进行预下载
例如下面的代码,即使不点击页面,a.js 也会预先下载。点击页面后,控制台依次输出'a'和'a.test'
- // main.js
- define(function(require, exports, module){
- document.onclick = function(){
- vara = require('js/a');
- a.test();
- }
- });
- define(function(require, exports, module){
- console.log('a');
- exports.test = function(){
- console.log('a.test');
- }
- })
能不能执行时再下载呢?类似于懒加载。有的,使用 require.async() 方法。
方法用来在模块内部异步加载模块,并在加载完成后执行指定回调
- require.async
- // main.js
- define(function(require, exports, module) {
- document.onclick = function() {
- require.async('./a',
- function(a) {
- a.test();
- });
- }
- });
- //a.js
- define(function(require, exports, module) {
- console.log('a');
- exports.test = function() {
- console.log('a.test');
- }
- })
【exports】
是一个对象,用来向外提供模块接口。与 CommonJS 的 exports 功能类似
- exports
- define(function(require, exports) {
- // 对外提供 foo 属性exports.foo ='bar';
- // 对外提供 doSomething 方法exports.doSomething = function() {};
- });
除了给
对象增加成员,还可以使用
- exports
直接向外提供接口,这种方式与 requireJS 的方式类似
- return
- define(function(require) {
- // 通过 return 直接提供接口
- return {
- foo: 'bar',
- doSomething: function() {}
- };
- });
如果
语句是模块中的唯一代码,还可简化为
- return
- define({
- foo: 'bar',
- doSomething: function() {}
- });
【module】
是一个对象,上面存储了与当前模块相关联的一些属性和方法
- module
- // main.js
- define(['./a'],
- function(require, exports, module) {
- console.log(module);
- })
module.uri 表示根据模块系统的路径解析规则得到的模块绝对路径
module.id 是模块的唯一标识,一般情况下没有在 define 中手写 id 参数时,module.id 的值就是 module.uri,两者完全相同
module.dependencies 是一个数组,表示当前模块的依赖
module.exports 是当前模块对外提供的接口。传给 factory 构造方法的 exports 参数是 module.exports 对象的一个引用。只通过 exports 参数来提供接口,有时无法满足开发者的所有需求。 比如当模块的接口是某个类的实例时,需要通过 module.exports 来实现
[注意] 对
的赋值需要同步执行,不能放在回调函数里。下面这样是不行的
- module.exports
- define(function(require, exports, module) {
- // 错误用法
- setTimeout(function() {
- module.exports = {
- a: "hello"
- };
- },
- 0);
- });
requireJS 通过 data-main 来设置入口,而 seaJS 则通过 sea.use() 来设置。sea.js 在下载完成后,会自动加载入口模块
- seajs.use(id, callback?)
[注意]
参数可选,省略时,表示无需回调
- callback
- <script src="sea.js">
- </script>
- <script>
- seajs.use('js/main');
- </script>
加载单个依赖,运行以下代码后,控制台输出'test'
- //index.html
- seajs.config({
- base: 'js'
- });
- seajs.use("main",
- function(a) {
- a.test();
- });
- // main.js
- define(['./a'],
- function(require, exports, module) {
- return {
- test: function() {
- console.log('test');
- }
- }
- })
加载多个依赖
- //并发加载模块 a 和模块 b,并在都加载完成时,执行指定回调
- seajs.use(['./a', './b'],
- function(a, b) {
- a.init();
- b.init();
- });
【DOMReady】
与
- seajs.use
事件没有任何关系。如果某些操作要确保在
- DOM ready
后执行,需要使用
- DOM ready
等类库来保证
- jquery
- seajs.use(['jquery', './main'],
- function($, main) {
- $(document).ready(function() {
- main.init();
- });
- });
【打包】
引入
时,可以把
- sea.js
与其他文件打包在一起,可提前合并好,或利用 combo 服务动态合并。无论哪一种方式,为了让
- sea.js
内部能快速获取到自身路径,推荐手动加上
- sea.js
属性
- id
- <script src="path/to/sea.js" id="seajsnode">
- </script>
加上
值,可以让
- seajsnode
直接获取到自身路径,而不需要通过其他机制去自动获取。这对性能和稳定性会有一定提升,推荐默认都加上
- sea.js
【路径】
如果不配置路径,在 requireJS 中,默认路径是 data-main 的所处目录,比如 data-main='js/main',则所处路径是'js'目录下
而 seaJS 则不同,它的默认路径是 seaJS 文件的所处目录,比如 seaJS 文件所处路径是'demo'目录下,进行如下入口设置后
- seajs.use('js/main');
说明 main.js 的目录为'demo/js/main.js'。如果 main.js 依赖于 a.js,且 a.js 与 main.js 处于同一目录下,则以下两种写法都正确
1、'demo' + 'js/a' = 'demo/js/a.js'
- // main.js
- define(['js/a'],
- function(require, exports, module) {
- })
2、'./'表示当前目录,即'demo/js',所以 './a' = 'demo/js/a.js'
- // main.js
- define(['./a'],
- function(require, exports, module) {
- })
在 requireJS 中使用 baseUrl 来配置基础路径,而在 seaJS 中使用 base。进行如下配置后,真实路径为'demo' + 'js' + 'main' = 'demo/js/main.js'
- seajs.config({
- base: 'js'
- });
- seajs.use("main");
【别名】
当模块标识很长时,可以使用
来简化
- alias
- seajs.config({
- alias: {
- 'jquery': 'jquery/jquery/1.10.1/jquery',
- 'app/biz': 'http://path/to/app/biz.js',
- }
- });
【目录】
当目录比较深,或需要跨目录调用模块时,可以使用
来简化书写
- paths
- seajs.config({
- paths: {
- 'gallery': 'https://a.alipayobjects.com/gallery',
- 'app': 'path/to/app',
- }
- });
AMD 是 RequireJS 在推广过程中对模块定义的规范化产出,CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。这些规范的实现都能达成浏览器端模块化开发的目的
AMD 与 CMD 主要有以下两点区别
1、所依赖模块的执行时机
对于依赖的模块,AMD 是提前执行,CMD 是延迟执行
AMD 在加载模块完成后就会执行该模块,所有模块都加载执行完后会进入 require 的回调函数,执行主逻辑,这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,看网络速度,哪个先下载下来,哪个先执行,但是主逻辑一定在所有依赖加载完成后才执行。不过,新版本的 RequireJS 也可以延迟执行
CMD 加载完某个依赖模块后并不执行,只是下载而已,在所有依赖模块加载完成后进入主逻辑,遇到 require 语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的。如果使用 require.async() 方法,可以实现模块的懒加载,即不执行不下载
2、CMD 推崇依赖就近,AMD 推崇依赖前置
- // CMDdefine(function(require, exports, module) {
- vara = require('./a')
- a.doSomething()
- // 此处略去 100 行
- varb = require('./b')// 依赖可以就近书写
- b.doSomething()
- // ... })
- // AMDdefine(['./a', './b'],function(a, b) {// 依赖必须一开始就写好
- a.doSomething()
- // 此处略去 100 行
- b.doSomething()
- ...
- })
当然,AMD 也支持 CMD 的写法,同时还支持将 require 作为依赖项传递
CommonJS、requireJS、seaJS 这三种模块化方案,并没有高低之分。随着各个方案的不断升级,语言方面相互借鉴,使用差异逐渐变小。以上三种库级别的模块化方案,需要引入额外的库,且所遵循的规范并不是标准组织制定的,权威性不足
随着 ES6 在语言层面上开始支持模块化,ES6 的模块化写法才是未来的模块化标准
来源: http://www.cnblogs.com/xiaohuochai/p/6879432.html