CommonJS,AMD,CMD和ES6的对比

December 17, 2023
测试
测试
测试
测试
9 分钟阅读

四种常见的规范

CommonJS

前端浏览器不支持,用于服务器,Nodejs中使用的是这个规范

exports.area = function(r) {
   return Math.PI * r * r;
}
exports.circleCumference= function(r) {
   return Math.PI * r * r;
}

CommonJS的核心思想就是通过 require 方法来同步加载所要依赖的其他模块,然后通过 exports 或者 module.exports 来导出需要暴露的接口。

AMD

浏览器端的模块,不能采用后端使用的CommonJS的”同步加载”(synchronous),只能采用”异步加载”(asynchronous),这就是AMD规范诞生的背景。

AMD是RequireJS在推广过程中对模块定义的规范化产出。

AMD规范则是非同步加载模块,允许指定回调函数。

AMD标准中,定义了下面两个API:

  • require([module], callback)
  • define(id, [depends], callback) 即通过define来定义一个模块,然后使用require来加载一个模块。 并且,require还支持CommonJS的模块导出方式。

test.js

define(['package/lib',...], function(lib) {
    function foo () {
        lib.log('hello world');
    }

    return {
      foo: foo
    }
});

require(['test'], function(test) {
  test.foo()
})

CMD

CMD是SeaJS在推广过程中对模块定义的规范化产出。

CMD是同步模块定义。

//所有模块都通过define来定义
define(function(require, exports, module) {  
  // 通过require引入依赖
  var $ = require('jquery');
  var Spinning = require('./spinning');
  exports.doSomething = ...
  module.exports = ...
})

二者的区别是前者是对于依赖的模块提前执行,而后者是延迟执行。 前者推崇依赖前置,而后者推崇依赖就近,即只在需要用到某个模块的时候再require。

ES6—export/import

在ES6中,我们可以使用 import 关键字引入模块,通过 exprot 关键字导出模块,功能较之于前几个方案更为强大,也是我们所推崇的,但是由于ES6目前无法在浏览器中执行,所以,我们只能通过babel将不被支持的import编译为当前受到广泛支持的 require

import App from './App.vue'
export default {
   props: ['num']
}

使用注意点

AMD和CMD区别

规范

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

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。 CMD 是 SeaJS 在推广过程中对模块定义的规范化产s出。 类似的还有 CommonJS Modules/2.0 规范,是 BravoJS 在推广过程中对模块定义的规范化产出。

这些规范的目的都是为了 JavaScript 的模块化开发,特别是在浏览器端的。 目前这些规范的实现都能达成浏览器端模块化开发的目的

区别:

  1. 定位有差异。 RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。 Sea.js 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 环境中。
  2. 遵循的规范不同。 RequireJS 遵循 AMD(异步模块定义)规范,Sea.js 遵循 CMD (通用模块定义)规范。规范的不同,导致了两者 API 不同。 Sea.js 更贴近 CommonJS Modules/1.1 和 Node Modules 规范。 CMD 推崇依赖就近,AMD 推崇依赖前置。看代码: // CMD define(function(require, exports, module) { var a = require('./a') a.doSomething() // 此处略去 100 行 var b = require('./b') // 依赖可以就近书写 b.doSomething() // ... }) // AMD 默认推荐的是 define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好 a.doSomething() // 此处略去 100 行 b.doSomething() //... }) 虽然 AMD 也支持 CMD 的写法,同时还支持将 require 作为依赖项传递,但 RequireJS 的作者默认是最喜欢上面的写法,也是官方文档里默认的模块定义写法。
  3. 推广理念有差异。 RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。 Sea.js 不强推,采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。
  4. 对开发调试的支持有差异。 Sea.js 非常关注代码的开发调试,有 nocache、debug 等用于调试的插件。 RequireJS 无这方面的明显支持。
  5. 插件机制不同。 RequireJS 采取的是在源码中预留接口的形式,插件类型比较单一。 Sea.js 采取的是通用事件机制,插件类型更丰富。
  6. 执行机制不同。 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。 不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。 CMD 推崇 as lazy as possible.
  7. API差异。 AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。 比如 AMD 里,require 分全局 require 和局部 require,都叫 require。 CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。 CMD 里,每个 API 都简单纯粹

ES6 import时如何正确使用{ }

导出变量/方法

export const A = 42;
export const api = {
  
};
export function myfunc() {
  
};

var myapi = {};
export {myapi};

导出默认

// 导出默认
export default 42;
export default {

};
export default function() {
  
};
var myapi = {};
export { myapi as default };

导入变量/方法

导入变量/方法的时候变量和方法名必须和导出时一致

import { A,api,myfunc,myapi } from './A'
// 导入设置别名
import { A as AA, myfunc as afunc } from './A'

导入默认

导入默认时变量和方法名可自定义

import A from './A'
// 导入设置别名
import { default as myApi } from './A';

详解

而我们这里要说的是在使用import语法引用模块时,如何正确使用{}

假如有一个B.js,想要通过import语法引用模块A.js,那么可以这么写:

// B.js
import A from './A'

而上面的代码生效的前提是,只有在如下A.js中有默认导出export default语法时才会生效。

也就是:

// A.js
export default 42

在这种不使用{}来引用模块的情况下,import模块时的命名是随意的

即如下引用命名都是正确的:

// B.js
import A from './A'
import MyA from './A'

因为它总是会解析到A.js中默认的export default


而下面是使用了花括号命名的方式{A}来导入A.js

import { A } from './A'

上面代码生效的前提是,只有在模块A.js中有如下命名导出Aexport name的代码,也就是:

export const A = 42

而且,在明确声明了命名导出后,那么在另一个js中使用{}引用模块时,import时的模块命名是有意义的,如下:

// B.js
import { A } from './A'                 // 正确,因为A.js中有命名为A的export
import { myA } from './A'               // 错误!因为A.js中没有命名为myA的export

要想上述代码正确执行,你需要明确声明每一个命名导出:

// A.js
export const myA = 42
export const myB = 43
export default 42

一个模块中只能有一个默认导出export default,但是却可以有任意命名导出(0个、1个、多个)

你也可以如下,一次性将他们导入

// B.js
import A, { myA, myB } from './A'

这里我们使用导入默认导出A,以及命名导出myAmyB

我们甚至可以在导入的时候重命名导入:

import A, { myA as AA, myB as BB } from './A'

也可以这样导出

var api = {

}

export function myfunc() {};  
export default function() {};  

export { api }
export { api as default };

分别对应的导入

import myApi, { api,myfunc } from './A'
// 上面的导入的myApi和下面的一样
import { default as myApi } from './A';  

myfunc();

总结:模块的默认导出通常是用在你期望该从模块中获取到任何想要的内容;而命名导出则是用于一些有用的公共方法,但是这些方法并不总是必要的。

继续阅读

更多来自我们博客的帖子

如何安装 BuddyPress
由 测试 December 17, 2023
经过差不多一年的开发,BuddyPress 这个基于 WordPress Mu 的 SNS 插件正式版终于发布了。BuddyPress...
阅读更多
Filter如何工作
由 测试 December 17, 2023
在 web.xml...
阅读更多
如何理解CGAffineTransform
由 测试 December 17, 2023
CGAffineTransform A structure for holding an affine transformation matrix. ...
阅读更多