CMD模块

devtools/2025/2/4 11:25:03/

概述

CMD(Common Module Definition) 是国内大牛玉伯在开发 SeaJS 的时候提出来的,属于 CommonJS 的一种规范,根据浏览器的异步环境做了自己的实现。它和 AMD 很相似,尽量保持简单,并与 CommonJS 和 Node.js 的 Modules 规范保持了很大的兼容性。

define 方法:定义模块

在CMD中,一个模块就是一个文件,格式为:define(factory)define 是一个全局函数,用来定义模块。

factory 函数

define 接受 factory 参数,factory 可以是一个函数,也可以是一个对象或字符串。

factory 为对象、字符串时,表示模块的接口就是该对象、字符串。比如可以如下定义一个 JSON 数据模块:

define({ "foo": "bar" });

也可以通过字符串定义模板模块:

define('I am a template. My name is {{name}}.');

factory 为函数时,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。factory 方法在执行时,默认会传入三个参数:requireexportsmodule

define(function(require, exports, module) {// 模块代码
});

define 也可以接受两个以上的参数,字符串 id 为模块标识,数组 deps 为模块依赖,格式为:define( id?, deps?, factory );具体使用如下:

define('hello', ['jquery'], function(require, exports, module) {// 模块代码
});

但是,带 iddeps 参数的 define 用法不属于 CMD 规范。CMD 推崇一个文件一个模块,所以经常就用文件名作为模块 id。CMD 推崇依赖就近,所以一般不在 define 的参数中写依赖,而在 factory 中写。

require 方法:加载模块

requirefactory 函数的第一个参数。require 是一个方法,接受模块标识作为唯一参数,用来获取其他模块提供的接口。

define(function(require, exports) {// 获取模块 a 的接口var a = require('./a');// 调用模块 a 的方法a.doSomething();
});
require.async(id, callback)

require.async 方法用来在模块内部异步加载模块,并在加载完成后执行指定回调。callback 参数可选。

define(function(require, exports, module) {  // 异步加载模块,在加载完成时,执行回调require.async(['./c', './d'], function(c, d) {c.doSomething();d.doSomething();});
});

require 是同步往下执行,require.async 则是异步回调执行。require.async 一般用来加载可延迟异步加载的模块。

define(function(require, exports, module) {  // 异步加载模块,在加载完成时,执行回调require.async(['./c', './d'], function(c, d) {c.doSomething();d.doSomething();});});
require.resolve(id)

使用模块系统内部的路径解析机制来解析并返回模块路径。该函数不会加载模块,只返回解析后的绝对路径。

define(function(require, exports) {console.log(require.resolve('./b'));// ==> http://example.com/path/to/b.js});

这可以用来获取模块路径,一般用在插件环境或需动态拼接模块路径的场景下。

exports 对象:暴露接口对象

exports 是一个对象,用来向外提供模块接口。

define(function(require, exports) {// 对外提供 foo 属性exports.foo = 'bar';// 对外提供 doSomething 方法exports.doSomething = function() {};});

除了给 exports 对象增加成员,还可以使用 return 直接向外提供接口。

define(function(require) {// 通过 return 直接提供接口return {foo: 'bar',doSomething: function() {}};
});

需要特别注的是,不能直接给 exports 赋值,例如下面是错误的写法:

define(function(require, exports) {// 错误用法!!!exports = {foo: 'bar',doSomething: function() {}};
});

exports 仅仅是 module.exports 的一个引用。而模块导出的时候,真正导出的是 module.exports,而不是 exports。在 factory 内部给 exports 重新赋值时, exports 就不再指向 module.exports 了。因此给 exports 赋值是无效的,正确的写法是用 return 或者给 module.exports 赋值:

define(function(require, exports, module) {// 正确写法module.exports = {foo: 'bar',doSomething: function() {}};
});

module 对象:本模块对象

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

module.id

模块的唯一标识。

define('id', [], function(require, exports, module) {// 模块代码
});

上面代码中,define 的第一个参数就是模块标识。

module.uri

根据模块系统的路径解析规则得到的模块绝对路径。

define(function(require, exports, module) {console.log(module.uri); // ==> http://example.com/path/to/this/file.js
});

一般情况下(没有在 define 中手写 id 参数时),module.id 的值就是 module.uri,两者完全相同。

module.dependencies

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

module.exports

表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取 module.exports 变量。传给 factory 构造方法的 exports 参数是 module.exports 对象的一个引用。只通过 exports 参数来提供接口,有时无法满足开发者的所有需求。 比如当模块的接口是某个类的实例时,需要通过 module.exports 来实现:

define(function(require, exports, module) {// exports 是 module.exports 的一个引用console.log(module.exports === exports); // true// 重新给 module.exports 赋值module.exports = new SomeClass();// exports 不再等于 module.exportsconsole.log(module.exports === exports); // false
});

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

// x.js
define(function(require, exports, module) {// 错误用法setTimeout(function() {module.exports = { a: "hello" };}, 0);
});

在 y.js 里有调用到上面的 x.js:

// y.js
define(function(require, exports, module) {var x = require('./x');// 无法立刻得到模块 x 的属性 aconsole.log(x.a); // undefined
});

define.cmd 属性

defin.cmd 属性是一个空对象,可用来判定当前页面是否有 CMD 模块加载器,其用法跟 AMD 的 denfine.amd 相似,写法如下:

if (typeof define === "function" && define.cmd) {// 有 Sea.js 等 CMD 模块加载器存在
}

CMD 和 AMD 的区别

  1. 对于依赖的模块 AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不通过)。

  2. AMD 推崇依赖前置(在定义模块的时候就要声明其依赖的模块),CMD 推崇依赖就近(只有在用到某个模块的时候再去 require ——按需加载)。

    //AMD
    define(['./a','./b'], function (a, b) {//依赖一开始就写好a.test();b.test();
    });//CMD
    define(function (requie, exports, module) {//依赖可以就近书写var a = require('./a');a.test();...//软依赖if (status) {var b = requie('./b');b.test();}
    });
    
  3. AMD 的 api 默认是一个当多个用,CMD 严格的区分推崇职责单一。例如:AMD 里 require 分全局的和局部的。CMD 里面没有全局的 require,提供 seajs.use() 来实现模块系统的加载启动。CMD 里每个 API 都简单纯粹。

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出,CMD 是 SeaJS 在推广过程中被广泛认知。RequireJs 出自 dojo 加载器的作者 James Burke,SeaJs 出自国内前端大师玉伯。二者的区别,玉伯在 12 年如是说:

RequireJS 和 SeaJS 都是很不错的模块加载器,两者区别如下:

  1. 两者定位有差异。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。SeaJS 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 服务器端

  2. 两者遵循的标准有差异。RequireJS 遵循的是 AMD(异步模块定义)规范,SeaJS 遵循的是 CMD (通用模块定义)规范。规范的不同,导致了两者API 的不同。SeaJS 更简洁优雅,更贴近 CommonJS Modules/1.1 和 Node Modules 规范。

  3. 两者社区理念有差异。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。SeaJS 不强推,而采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。

  4. 两者代码质量有差异。RequireJS 是没有明显的 bug,SeaJS 是明显没有 bug。

  5. 两者对调试等的支持有差异。SeaJS 通过插件,可以实现 Fiddler 中自动映射的功能,还可以实现自动 combo 等功能,非常方便便捷。RequireJS无这方面的支持。

  6. 两者的插件机制有差异。RequireJS 采取的是在源码中预留接口的形式,源码中留有为插件而写的代码。SeaJS 采取的插件机制则与 Node 的方式一致开放自身,让插件开发者可直接访问或修改,从而非常灵活,可以实现各种类型的插件。

优点: 同样实现了浏览器端的模块化加载。 可以按需加载,依赖就近。

缺点: 依赖SPM打包,模块的加载逻辑偏重。


http://www.ppmy.cn/devtools/155982.html

相关文章

解决注入线程池的栈溢出问题

文章目录 1.问题产生2.问题解决 1.问题产生 在使用sleuth的时候,需要注入线程池,他才会自动包装,实现traceId的传递,但是突然启动时出现了栈溢出的问题 2.问题解决 根据报错,发现是Gson序列化相关的问题&#xff0c…

【精选】基于数据挖掘的招聘信息分析与市场需求预测系统 职位分析、求职者趋势分析 职位匹配、人才趋势、市场需求分析数据挖掘技术 职位需求分析、人才市场趋势预测

博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…

4 [危机13小时追踪一场GitHub投毒事件]

事件概要 自北京时间 2024.12.4 晚间6点起, GitHub 上不断出现“幽灵仓库”,仓库中没有任何代码,只有诱导性的病毒文件。当天,他们成为了 GitHub 上 star 增速最快的仓库。超过 180 个虚假僵尸账户正在传播病毒,等待不…

C++游戏开发实战:从引擎架构到物理碰撞

📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 1. 引言 C 是游戏开发中最受欢迎的编程语言之一,因其高性能、低延迟和强大的底层控制能力,被广泛用于游戏…

在AWS上使用KMS客户端密钥加密S3文件,同时支持PySpark读写和Snowflake导入

现有AWS EMR集群上运行PySpark代码,可以读写S3上的数据文件,Snowflake数据仓库也需要导入S3上的文件到表。现在要用AWS KMS有客户端密钥加密S3上的文件,同时允许PySpark代码,可以读写S3上的数据文件,Snowflake数据仓库…

信息学奥赛一本通 2113:【24CSPJ普及组】小木棍(sticks) | 洛谷 P11229 [CSP-J 2024] 小木棍

【题目链接】 ybt 2113:【24CSPJ普及组】小木棍(sticks) 洛谷 P11229 [CSP-J 2024] 小木棍 【题目考点】 1. 思维题,找规律 【解题思路】 解法1:找规律 该题为:求n根木棍组成的无前导0的所有可能的数…

Hot100之图论

200岛屿数量 题目 思路解析 把访问过的格子插上棋子 思想是先污染再治理,我们有一个inArea()函数,是判断是否出界了 我们先dfs()放各个方向遍历,然后我们再把这个位置标为0 我们岛屿是连着…

WPF进阶 | WPF 数据绑定进阶:绑定模式、转换器与验证

WPF进阶 | WPF 数据绑定进阶:绑定模式、转换器与验证 一、前言二、WPF 数据绑定基础回顾2.1 数据绑定的基本概念2.2 数据绑定的基本语法 三、绑定模式3.1 单向绑定(One - Way Binding)3.2 双向绑定(Two - Way Binding)…