前端模块化规范 ---AMD、CMD、ES6、CommonJS

495 阅读4分钟

模块化的开发方式可以提高代码复用率,方便进行代码的管理。通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数。目前流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系统。

一、CommonJS

Node.js是commonJS规范的主要实践者,它有四个重要的环境变量为模块化的实现提供支持:module、exports、require、global。实际使用时,用module.exports定义当前模块对外输出的接口(不推荐直接用exports),用require加载模块。

  • 通过require引入基础数据类型时,属于复制该变量。
  • 通过require引入复杂数据类型时,数据浅拷贝该对象。
  • 出现模块之间的循环引用时,会输出已经执行的模块,而未执行的模块不输出(比较复杂)
  • CommonJS模块默认export的是一个对象,即使导出的是基础数据类型
// 定义模块math.js
var basicNum = 0;
function add(a, b) {
  return a + b;
}
module.exports = { //在这里写上需要向外暴露的函数、变量
  add: add,
  basicNum: basicNum
}

// 引用自定义的模块时,参数包含路径,可省略.js
var math = require('./math');
math.add(2, 5);

// 引用核心模块时,不需要带路径
var http = require('http');
http.createService(...).listen(3000);

commonJS用同步的方式加载模块。在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。

二、AMD

AMD规范采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。这里介绍用require.js实现AMD规范的模块化:用require.config()指定引用路径等,用define()定义模块,用require()加载模块。

三、CMD

四、ES6 Module

ES6 在语言标准的层面上,实现了模块功能。其模块功能主要由两个命令构成:export和import。

  • export命令用于规定模块的对外接口
  • import命令用于输入其他模块提供的功能。

ES Module与 CommonJS 模块的差异

  • 1、Commonjs是拷贝输出,ES6模块化是引用输出
  • 2、Commonjs是运行时同步加载,ES6模块化是编译时异步加载
  • 3、Commonjs可以修改值,ES Module的值不可修改
  • 4、Commonjs是动态语法可写在函数体中,ES6模块化静态语法只能写在顶层
  • 5、Commonjs的this是当前模块本身,ES6模块化的this是undefined
  • cjs不可以tree shaking,esm支持tree shaking

AMD与CMD的差异

模块化特点
AMD1、AMD的api默认一个当多个用 2、依赖前置,异步执行
CMD1、CMD的api严格区分,推崇职责单一 2、依赖就近,按需加载,同步执行

import与require的区别?

遵循规范:

  • require 是 AMD规范引入方式
  • import是es6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法

调用时间:

  • require是运行时调用,所以require理论上可以运用在代码的任何地方(虽然这么说但是还是一般放开头)
  • import是编译时调用,【由于是编译时加载,所以import命令会提升到整个模块的头部】

规范不一样:

  • require是CommonJs,AMD,CMD规范中定义的模块请求方式,

  • import是es6规范定义的模块请求方式,

从规范与实现定义来说 require是动态加载import是静态加载

从底层的运行来讲,require是在程序运行的时候去解析,import是在编译的时候去做解析请求包,

require是请求整个包对象而import是只请求模块中需要的请求的部分。现在import应该还只能算是ES6的语法规范,babel打包的话打出来应该还是跟require没有本质区别的。

CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用