前面的话

  CMD(Common Module Definition)表示通用模块定义,该规范是国内发展出来的,由阿里的玉伯提出。就像AMD有个requireJS,CMD有个浏览器的实现SeaJS,SeaJS和requireJS一样,都是javascript的模块化解决方案。本文将详细介绍CMD和seaJS

CMD

  在Sea.js中,所有JavaScript模块都遵循CMD(Common Module Definition)模块定义规范。该规范明确了模块的基本书写格式和基本交互规则

  AMD规范简单到只有一个API,即define函数

  1. define([module-name?], [array-of-dependencies?], [module-factory-or-object]);

  module-name: 模块标识,可以省略

  array-of-dependencies: 所依赖的模块,可以省略

  module-factory-or-object: 模块的实现,或者一个JavaScript对象

  CMD规范也与之类似,只不过第三个参数factory的实现方式不同。在CMD规范中,一个模块就是一个文件。代码的书写格式如下

  1. define(id?, deps?, factory)

  与AMD规范类似,define是一个全局函数,用来定义模块。字符串 id 表示模块标识,数组 deps 是模块依赖。这两个参数可以省略,通常由构建工具自动生成

  通常地,define()方法的第三个参数factory是一个函数,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。factory 方法在执行时,默认会传入三个参数:requireexports 和 module

  [注意]factory()方法的参数如果不需要,可以省略,但不可以修改,如修改为'a'、'b'、'c',也不可以改变其参数的顺序。在函数内部,也不能对参数名重新赋值,如'var a = require; '

  1. define(function(require, exports, module) {
  2.  
  3. // 模块代码
  4.  
  5. });

【require】

  require 是 factory 函数的第一个参数。require 是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口。通俗地说,通过require()方法来调用其他模块的属性或方法

  1. define(function(require, exports, module) {
  2.  
  3. // 获取模块 a 的接口
  4. var a = require('./a');
  5.  
  6. // 调用模块 a 的方法
  7. a.doSomething();
  8.  
  9. });

  这个require()方法的实现和功能都特别类似于CommonJS中的require()方法。或许,有人会有疑惑,require()不是一个同步方法吗?在CommonJS中是的,在seaJS中也可以这么说,但并不完整。更合理的说法应该是,模块内的同步加载,实际表现为对模块a进行预下载

  例如下面的代码,即使不点击页面,a.js也会预先下载。点击页面后,控制台依次输出'a'和'a.test'

  1. // main.js
  2. define(function(require, exports, module){
  3. document.onclick = function(){
  4. var a = require('js/a');
  5. a.test();
  6. }
  7. });
  8. define(function(require, exports, module){
  9. console.log('a');
  10. exports.test = function(){
  11. console.log('a.test');
  12. }
  13. })

  能不能执行时再下载呢?类似于懒加载。有的,使用require.async()方法。require.async 方法用来在模块内部异步加载模块,并在加载完成后执行指定回调

  1. // main.js
  2. define(function(require, exports, module){
  3. document.onclick = function(){
  4. require.async('./a',function(a){
  5. a.test();
  6. });
  7. }
  8. });
  9. //a.js
  10. define(function(require, exports, module){
  11. console.log('a');
  12. exports.test = function(){
  13. console.log('a.test');
  14. }
  15. })

【exports】

  exports 是一个对象,用来向外提供模块接口。与CommonJS的exports功能类似

  1. define(function(require, exports) {
  2.  
  3. // 对外提供 foo 属性
  4. exports.foo = 'bar';
  5.  
  6. // 对外提供 doSomething 方法
  7. exports.doSomething = function() {};
  8.  
  9. });

  除了给 exports 对象增加成员,还可以使用 return 直接向外提供接口,这种方式与requireJS的方式类似

  1. define(function(require) {
  2.  
  3. // 通过 return 直接提供接口
  4. return {
  5. foo: 'bar',
  6. doSomething: function() {}
  7. };
  8.  
  9. });

  如果 return 语句是模块中的唯一代码,还可简化为

  1. define({
  2. foo: 'bar',
  3. doSomething: function() {}
  4. });

【module】

  module 是一个对象,上面存储了与当前模块相关联的一些属性和方法

  1. // main.js
  2. define(['./a'],function(require, exports, module){
  3. console.log(module);
  4. })

  module.uri表示根据模块系统的路径解析规则得到的模块绝对路径

  module.id是模块的唯一标识,一般情况下没有在define中手写id参数时,module.id的值就是module.uri,两者完全相同

  module.dependencies是一个数组,表示当前模块的依赖

  module.exports是当前模块对外提供的接口。传给factory构造方法的exports参数是module.exports对象的一个引用。只通过exports参数来提供接口,有时无法满足开发者的所有需求。 比如当模块的接口是某个类的实例时,需要通过module.exports来实现

  [注意]对module.exports的赋值需要同步执行,不能放在回调函数里。下面这样是不行的

  1. define(function(require, exports, module) {
  2. // 错误用法
  3. setTimeout(function() {
  4. module.exports = { a: "hello" };
  5. }, );
  6. });

入口

  requireJS通过data-main来设置入口,而seaJS则通过sea.use()来设置。sea.js 在下载完成后,会自动加载入口模块

  1. seajs.use(id, callback?)

  [注意]callback参数可选,省略时,表示无需回调

  1. <script src="sea.js"></script>
  2. <script>
  3. seajs.use('js/main');
  4. </script>

  加载单个依赖,运行以下代码后,控制台输出'test'

  1. //index.html
  2. <script src="sea.js"></script>
  3. <script>
  4. seajs.config({
  5. base: 'js'
  6. });
  7. seajs.use("main",function(a){
  8. a.test();
  9. });
  10. </script>
  11.  
  12. // main.js
  13. define(['./a'],function(require, exports, module){
  14. return {
  15. test : function(){
  16. console.log('test');
  17. }
  18. }
  19. })

  加载多个依赖

  1. //并发加载模块 a 和模块 b,并在都加载完成时,执行指定回调
  2. seajs.use(['./a', './b'], function(a, b) {
  3. a.init();
  4. b.init();
  5. });

【DOMReady】

  seajs.useDOM ready事件没有任何关系。如果某些操作要确保在DOM ready后执行,需要使用jquery等类库来保证

  1. seajs.use(['jquery', './main'], function($, main) {
  2. $(document).ready(function() {
  3. main.init();
  4. });
  5. });

【打包】

  引入 sea.js 时,可以把 sea.js 与其他文件打包在一起,可提前合并好,或利用 combo 服务动态合并。无论哪一种方式,为了让 sea.js 内部能快速获取到自身路径,推荐手动加上 id 属性

  1. <script src="path/to/sea.js" id="seajsnode"></script>

  加上 seajsnode 值,可以让 sea.js 直接获取到自身路径,而不需要通过其他机制去自动获取。这对性能和稳定性会有一定提升,推荐默认都加上

配置

【路径】

  如果不配置路径,在requireJS中,默认路径是data-main的所处目录,比如data-main='js/main',则所处路径是'js'目录下

  而seaJS则不同,它的默认路径是seaJS文件的所处目录,比如seaJS文件所处路径是'demo'目录下,进行如下入口设置后

  1. 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'

  1. // main.js
  2. define(['js/a'],function(require, exports, module){
  3.  
  4. })

  2、'./'表示当前目录,即'demo/js',所以 './a' = 'demo/js/a.js'

  1. // main.js
  2. define(['./a'],function(require, exports, module){
  3.  
  4. })

  在requireJS中使用baseUrl来配置基础路径,而在seaJS中使用base。进行如下配置后,真实路径为 'demo' + 'js' + 'main' = 'demo/js/main.js'

  1. <script src="sea.js"></script>
  2. <script>
  3. seajs.config({
  4. base: 'js'
  5. });
  6. seajs.use("main");
  7. </script>

【别名】

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

  1. seajs.config({
  2. alias: {
  3. 'jquery': 'jquery/jquery/1.10.1/jquery',
  4. 'app/biz': 'http://path/to/app/biz.js',
  5. }
  6. });

【目录】

  当目录比较深,或需要跨目录调用模块时,可以使用 paths 来简化书写

  1. seajs.config({
  2. paths: {
  3. 'gallery': 'https://a.alipayobjects.com/gallery',
  4. 'app': 'path/to/app',
  5. }
  6. });

与AMD区别

  AMD 是 RequireJS 在推广过程中对模块定义的规范化产出,CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。这些规范的实现都能达成浏览器端模块化开发的目的

  AMD与CMD主要有以下两点区别

  1、所依赖模块的执行时机

  对于依赖的模块,AMD是提前执行,CMD是延迟执行

  AMD在加载模块完成后就会执行该模块,所有模块都加载执行完后会进入require的回调函数,执行主逻辑,这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,看网络速度,哪个先下载下来,哪个先执行,但是主逻辑一定在所有依赖加载完成后才执行。不过,新版本的RequireJS也可以延迟执行

  CMD加载完某个依赖模块后并不执行,只是下载而已,在所有依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的。如果使用require.async()方法,可以实现模块的懒加载,即不执行不下载

  2、CMD推崇依赖就近,AMD推崇依赖前置

  1. // CMD
  2. define(function(require, exports, module) {
  3. var a = require('./a')
  4. a.doSomething()
  5. // 此处略去 100 行
  6. var b = require('./b') // 依赖可以就近书写
  7. b.doSomething()
  8. // ...
  9. })
  1. // AMD
  2. define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
  3. a.doSomething()
  4. // 此处略去 100 行
  5. b.doSomething()
  6. ...
  7. })

  当然,AMD也支持CMD的写法,同时还支持将require作为依赖项传递

最后

  CommonJSrequireJSseaJS这三种模块化方案,并没有高低之分。随着各个方案的不断升级,语言方面相互借鉴,使用差异逐渐变小。以上三种库级别的模块化方案,需要引入额外的库,且所遵循的规范并不是标准组织制定的,权威性不足

  随着ES6在语言层面上开始支持模块化,ES6的模块化写法才是未来的模块化标准

CMD和seaJS的更多相关文章

  1. JS模块化规范CMD之SeaJS

    1. 在接触规范之前,我们用模块化来封装代码大多为如下: ;(function (形参模块名, 依赖项, 依赖项) { // 通过 形参模块名 修改模块 window.模块名 = 形参模块名 })(w ...

  2. AMD规范(RequireJS)、CMD规范(SeaJS)、CommonJS(BravoJS)规范的辨析

    首先,AMD,CMD,CommonJS都实现了文件模块化. 对于依赖的模块:AMD是提前执行:CMD是延迟执行: AMD是依赖前置,CMD是依赖就近: AMD官方解释:https://github.c ...

  3. JavaSript模块规范 - AMD规范与CMD规范介绍

    JavaSript模块化   在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发?       模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题 ...

  4. AMD与CMD(转载)

    JavaSript模块化   在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发?       模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题 ...

  5. JavaSript模块规范 - AMD规范与CMD规范介绍(转)

    JavaSript模块规范 - AMD规范与CMD规范介绍 JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发? 模块化是指在解决某一个复杂问题或者 ...

  6. 认识AMD、CMD、UMD、CommonJS

    0.导言 JavaScript的生态系统一直在稳步增长,当各种组件混合使用时,就可能会发现不是所有的组件都能“和平共处”,为了解决这些问题,各种模块规范就出来了. 1.AMD(Asynchromous ...

  7. CommonJS,AMD,CMD区别

    学得比较晕,再次看commonjs,amd, cmd时好像还是没完全弄清楚,今天再整理一下: commonjs是用在服务器端的,同步的,如nodejs amd, cmd是用在浏览器端的,异步的,如re ...

  8. AMD and CMD

    AMD 规范在这里:https://github.com/amdjs/amdjs-api/wiki/AMDCMD 规范在这里:https://github.com/seajs/seajs/issues ...

  9. JavaSript模块化-AMD规范与CMD规范

    JavaScript模块化 在了解AMD,CMD规范前,先来简单地了解下什么是模块化,模块化开发. 模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解以之处 ...

随机推荐

  1. laravel框架常用目录路径

    app_path() app_path函数返回app目录的绝对路径:$path = app_path(); 你还可以使用app_path函数为相对于app目录的给定文件生成绝对路径:$path = a ...

  2. grep的用法笔记

    1.grep搜索不保护字符串的命令格式如下: #带-v参数 jevan@df ~ grep "agc" -vRn ./

  3. ViewPager—01引导页的制作

    布局文件 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:t ...

  4. Crgwin 简介及安装

    Crgwin 简介 Cygwin是一个在windows平台上运行的类UNIX模拟环境,是cygnus solutions公司开发的自由软件(该公司开发的著名工具还有eCos,不过现已被Redhat收购 ...

  5. Redis分布式锁

    Redis分布式锁 分布式锁是许多环境中非常有用的原语,其中不同的进程必须以相互排斥的方式与共享资源一起运行. 有许多图书馆和博客文章描述了如何使用Redis实现DLM(分布式锁管理器),但是每个库都 ...

  6. okHttp基础用法

    获取okHttp..jar.包 1.联网获取jar包 2.本地添加 okHttp的使用 get请求 1.创建okHttpClient对象new OkHttpClient(); 2.创建一个请求对象Re ...

  7. 手把手视频:万能开源Hawk抓取动态网站

    Hawk是沙漠之鹰历时五年开发的开源免费网页抓取工具(爬虫),无需编程,全部可视化. 自从上次发布Hawk 2.0过了小半年,可是还是有不少朋友通过邮件或者微信的方式询问如何使用.看文档还是不如视频教 ...

  8. salt-stack部署

    saltstack部署   环境准备 [root@server elasticsearch]# cat /etc/redhat-release CentOS release 6.6 (Final)[r ...

  9. 【HDOJ 2150】线段交叉问题

    Pipe Time Limit : 1000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submissi ...

  10. Elasticsearch搜索之cross_fields分析

    cross_fields类型采用了一种以词条为中心(Term-centric)的方法,这种方法和best_fields及most_fields采用的以字段为中心(Field-centric)的方法有很 ...