CommonJS,AMD,CMD和ES6的对比

四种常见的规范

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 是 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();

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏salesforce零基础学习

Salesforce LWC学习(六) @salesforce & lightning/ui*Api Reference

上一篇中我们在demo中使用了很多的 @salesforce 以及 lightning/ui*Api的方法,但是很多没有细节的展开。其实LWC中针对这些modu...

9830
来自专栏salesforce零基础学习

Salesforce LWC学习(七) Navigation & Toast

上一篇我们介绍了针对LWC中常用的LDS的适配的wire service以及@salesforce模块提供的相关的service,其实LWC中还提供其他的好用的...

11140
来自专栏安恒网络空间安全讲武堂

[CSAW CTF'18] web writeup

后来才明白原来题目名就已经是hint了, 进去之后就会发现输入括号和没有括号是有差别的, 然后想起来最近的noxCTF刚刚做过一个LDAP注入

6830
来自专栏不想当开发的产品不是好测试

认识map-reduce

1)MapReduce从HDFS中分割读取Split文件,通过Inputformat交给Mapper来处理。Split是MapReduce中最小的计算单元,一个...

9340
来自专栏葡萄城控件技术团队

带你深入了解NPM——NPM初学者指南

前段时间,我们邀请了我们“城内”(葡萄城)资深开发工程师刘涛为大家分享了一次干货满满的关于Electron线上公开课,在课程过程中有不少同学对于NPM的概念和用...

9120
来自专栏Jerry的SAP技术分享

ABAP Netweaver体内的那些寄生式编程语言

Jerry最近看到朋友圈里一位朋友分享的一张寄居蟹的照片,对于Jerry这种在内地长大的又很宅的人来说,没有机会看到寄居蟹,所以觉得很新鲜:

7900
来自专栏窗户

map的实现和柯里化(Currying)

  对于函数式编程来说,map/reduce/filter这几个算子非常重要,其中有的语言不是reduce而是fold,但功能基本一样,不过reduce的迭代一...

11120
来自专栏code秘密花园

【开源推荐】 手绘风格的 JS 图表库

手绘风格的设计给人一种很可爱的感觉,看了这些图表你会发现数据也可以以萌萌哒的形式展示。

10630
来自专栏salesforce零基础学习

Salesforce LWC学习(三) import & export / api & track

我们使用vs code创建lwc 时,文件会默认生成包含 template作为头的html文件,包含了 import LightningElement的 js...

12920
来自专栏葡萄城控件技术团队

JS中3种风格的For循环有什么异同?

在学习任何开发语言时候,for循环是必不可少的一种语法,可能所有开发人员都会使用它。它非常经典,以至于每个开发语言都至少包括一种关于循环的语法版本。不过,在Ja...

5820

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励