对于大型Web应用或者项目,动辄上万行的代码,给开发和后期维护带来了不小的麻烦。因此,需要有一种规范化的模块管理机制,帮助开发者集中处理模块的定义与调用关系。
在ES6正式出台前,已经有不少人致力于推出适合Web开发的模块化管理标准,CommonJS、AMD和CMD就是其中的成功代表。
以下为我查阅资料后,对三者最直观的理解:
CommonJS
通常被应用于服务器,实现代表:NodeJS。NodeJS相信大家都不陌生,它的出现成功将Javascript应用到了服务器端,使得前段开发者也能较为省力得转向后端开发,而无需学习一门新语言。
在服务器端,模块的加载和执行都在本地完成,因此,CommonJS并不要求模块加载的异步化。
模块定义与加载
- 对于CommonJS,模块以文件为单位存在,模块实现全部位于该文件内部
- 在文件末尾,需要把将要导出的方法添加到module.exports对象上
- 随后在其他文件(模块)中,可以使用require()方法加载需要的模块,该方法要求一个参数:可以是一个模块名(此时加载的是默认提供的模块),或者是一个URL。如果是URL,其可以是相对路径或绝对路径,或者是一个指定的路径(由Node自行解析并寻找)。关于文件的后缀名,默认为“.js”,因此参数中可以省略后缀名;而如果指定的模块未发现,则会继续寻找以“.json”和“.node”为后缀的文件(其中以“.node”为后缀的文件将会以编译后的二进制文件格式解析)。
AMD
AMD(异步模块加载)和CMD通常用于浏览器端,它们与CommonJS最明显的不同就是异步加载。AMD的实现代表是require.js,它也是require.js在推广过程中的附加产物。
模块定义与加载
- AMD模块不一定以文件为单位,小模块集群也是允许的;但基本上在开发过程中,还是应当以文件的形式进行管理
- 模块的定义由define()方法确定,该方法接受两个参数:
- 一是当前模块的依赖模块,以数组形式传递
- 二是当前模块的实现,以回调形式传递。一中的依赖通过参数传给回调。在回调末尾,以对象的形式返回需要公开的方法组。
- 模块的配置使用require.config()方法,它接受一个对象类型的参数,该对象需要配置一些属性:baseUrl,paths,shim等
- baseUrl:所有模块查找的根路径;未设置时默认是使用require.js的HTML文件所在目录。或者可以使用data-main属性在加载require.js的标签中定义根路径。
- callback:当前依赖全部加载完成后的回调函数
- config:将Application级别的配置信息传递给模块时使用。传递时使用module.config()方法。
requirejs.config({ config: { ‘bar‘: { size:‘large‘ }, ‘baz‘: { color: ‘blue‘ } } }); //bar.js直接使用module模块 define(function (require, exports, module) { var size = module.config().size; }); //baz.js使用了一个依赖数组,并要求一个特殊的依赖“module” define([‘module‘], function(module) { var color = module.config().color; });
当“some/newmodule”调用了“require(‘foo‘)”,它将获取到foo1.2.js文件;而当“some/oldmodule”调用“`require(‘foo‘)”时它将获取到foo1.0.js。
- exports:模块的输出
- init:模块的初始化工作
- paths:模块文件的路径。当paths中以/开头、或以.js结尾、或包含协议时(如http://...),其路径不在根路径下。
- shim:需要未用define()方法定义的模块时使用。值为一组模块对象,每个对象分别有一些属性:deps,exports,init等。值得注意的是,shim只是定义了模块的依赖关系,仍然需要通过define+require的方式调用。
- deps:模块的依赖数组
- exports:模块的输出
- map:对于给定的模块前缀,使用一个不同的模块ID来加载该模块。可以使用*号作为默认配置,且该配置可以被更具体的配置覆盖。
requirejs.config({ map: { ‘some/newmodule‘: { ‘foo‘: ‘foo1.2‘ }, ‘some/oldmodule‘: { ‘foo‘: ‘foo1.0‘ } } });
- 更多参数请戳 参考链接
CMD
CMD(通用模块加载)是sea.js在推广过程中的产出,它与AMD主要有以下几点区别(来自seajs的官方阐述):
- 定位有差异。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。Sea.js 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 环境中。
- 遵循的规范不同。RequireJS 遵循 AMD(异步模块定义)规范,Sea.js 遵循 CMD (通用模块定义)规范。规范的不同,导致了两者 API 不同。Sea.js 更贴近 CommonJS Modules/1.1 和 Node Modules 规范。
- 推广理念有差异。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。Sea.js 不强推,采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。
- 对开发调试的支持有差异。Sea.js 非常关注代码的开发调试,有 nocache、debug 等用于调试的插件。RequireJS 无这方面的明显支持。
- 插件机制不同。RequireJS 采取的是在源码中预留接口的形式,插件类型比较单一。Sea.js 采取的是通用事件机制,插件类型更丰富。
- AMD提倡依赖前置,也就是在一开始就定义加载。而CMD则推崇依赖就近,即就在使用到模块的代码前面进行依赖加载。这里就需要注意了,使用requirejs会按照模块的依赖顺序进行加载,即模块不会按照代码里的顺序进行加载,可能造成意外地结果。
这形成了两种截然不同的加载方式:“预加载”和“懒加载”。AMD提前将所有模块的所有依赖加载完成后,才开始执行主流程;而CMD是遇到一个依赖开始加载,完成后执行当前流程,遇到下一个依赖再次去加载,然后再执行下一步。当然,同一模块的依赖之间仍然是异步加载的。
模块定义与加载
流程与AMD类似,不再赘述,只简单介绍seajs的API。
- cache:查看当前模块系统中的模块信息
- config:配置模块参数,参数列表:
- alias:模块别名
- base:根路径
- paths:模块文件所在路径
- vars:变量配置。比如需要在运行时确定模块路径的情况:
seajs.config({ // 变量配置 vars: { ‘locale‘: ‘zh-cn‘ } }); define(function(require, exports, module) { var lang = require(‘./i18n/{locale}.js‘); //=> 加载的是 path/to/i18n/zh-cn.js });
使用变量需要用花括号包围,如例中的{locale}。
- data:查看 seajs 所有配置以及一些内部变量的值,可用于插件开发。当加载遇到问题时,也可用于调试。
- resolve:利用模块系统的内部机制对传入的字符串参数进行路径解析。
- use:加载模块。seajs.use(id/url/urlArray, callback)
时间: 2024-11-05 13:42:10