对CommonJS、AMD、CMD简单粗暴的理解

  对于大型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)
原文地址:https://www.cnblogs.com/cjc917/p/7492569.html