javascript的模块系统(CommonJS,ES6 module,AMD,CMD,UMD,requireJs)

705 阅读3分钟

javascript的模块系统错综复杂,比较常见的有AMD(requireJs),CMD(sea.js),CommonJS,UMD, ES6 module等等, 本文主要介绍CommonJS与ES module的相关知识及比较二者。

CommonJS

简介

CommonJS用于后端是node.js的模块系统,模块同步加载, 通过require导入模块,通过exports导出模块,模块的载入是动态的,返回一个对象,对象的属性方法也是在运行时确定的。

语法

导入模块
  const foo=require('./foo.js')
  console.log(foo.age)//1
导出模块
//foo.js
// Export by export
module.exports.age=1
module.exports.foo=function(){}
exports.a='hello'

//Whole export
module.exports={age:1,a:'hello',foo:function(){}}

//Cannot use 'exports' for overall export. Cannot use' exports' for import
exports={age:1,a:'hello',foo:function(){}}

ES module

简介

ES module服务端和客户端都适用,模块是编译时输出接口,接口只是一种静态定义,在代码静态解析阶段就会生成,也支持动态导入模块,语法上通过import导入模块,通过export导出模块。

语法

导出模块
// 代码来自https://programmer.help/blogs/the-difference-between-commonjs-and-es6-module.html#j1
 export * from 'module';
 export {name1,name2,....,nameN } from 'module';//Redirect named exports 从module中再导出
 export {import1 as name1,import2 as name2,...,nameN } from 'module'; //Redirect rename export // 重命名
export {name1,name2,..,nameN};//Bind named exports with previously declared variable names // 导出之前声明的变量
export let name1='name1';//Declare named exports or var,const,function,function*,class
export default expression;//Default export
export default function(){...}//Or function*,class
export default function name1(){...}//Or function*,class
export { name1 as default,...};//Rename to default export
导入模块
 //Named export module.js
  let a=1,b=2
  export {a,b}
  export let c=3
 //Named import main.js
 import {a,b,c} from 'module';//a:1 b:2 c:3
 import {a as newA,b,c as newC } from 'module';//newA:1 b:2 newC:3

 // Export module.js by default
 export default 1
 
 // Import main.js by default
 import defaultExport from 'module';//defaultExport:1
 
 // Mixed export module.js
 let a=1
 export {a}
 const b=2
 export {b}
 export let c=3
 export default [1,2,3]

// module.js
Array.prototype.remove=function(){}

//Side effects only run one module
import 'module';//Execute module without exporting value multiple times call module.js only once

//Dynamic import (asynchronous import) 动态导入
var promise=import('module');

CommonJS与ES module的区别

模块导出

  1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用(default 除外)
// 代码来自https://es6.ruanyifeng.com/#docs/module-loader#ES6-%E6%A8%A1%E5%9D%97%E4%B8%8E-CommonJS-%E6%A8%A1%E5%9D%97%E7%9A%84%E5%B7%AE%E5%BC%82
// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};
// main.js
var mod = require('./lib');

console.log(mod.counter);  // 3
mod.incCounter();
console.log(mod.counter); // 3


// lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}

// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4
  1. CommonJs只能导出一个值, ES6 Module可以导出多个值
// CommonJS
module.exports = {a: 1}
// ES6 Module
export default () => {}
let a =1, b = 2, c =3
export {a, b, c}

模块导入

  1. CommonJS可以通过条件判断动态引入,ES6的静态语法只能写在文件顶部
let a = 1
if(a){
    let b = require('./b') // CommonJS可以这样写
    import {b} from './b' // ES6 这样就是错的
}

this指向

CommonJS 指向当前模块,ES6模块指向undefined

// CommonJS
module.exports = {
    test: function(){
        console.log(this) // 指向module.exports
    }
}
// ES6 module
export function test(){
    console.log(this) // 指向undefined
}

循环加载

“循环加载”(circular dependency)指的是,a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本。es6.ruanyifeng.com/

可以点击上面的链接,看阮一峰老师的教程,它讲的比较透彻,下面我就直接贴他的示例代码了。

CommonJS

// a.js
exports.done = false;
var b = require('./b.js');
console.log('在 a.js 之中,b.done = %j', b.done);
exports.done = true;
console.log('a.js 执行完毕');
// b.js
exports.done = false;
var a = require('./a.js');
console.log('在 b.js 之中,a.done = %j', a.done);
exports.done = true;
console.log('b.js 执行完毕');
// main.js
var a = require('./a.js');
var b = require('./b.js');
console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done);
// node main
//在 b.js 之中,a.done = false
//b.js 执行完毕
//在 a.js 之中,b.done = true
//a.js 执行完毕
//在 main.js 之中, a.done=true, b.done=true

ES6 module

// a.mjs
import {bar} from './b';
console.log('a.mjs');
console.log(bar());
function foo() { return 'foo' }
export {foo};

// b.mjs
import {foo} from './a';
console.log('b.mjs');
console.log(foo());
function bar() { return 'bar' }
export {bar};
// node --experimental-modules a.mjs
//b.mjs
//foo
//a.mjs
//bar

本文主要介绍了CommonJS与ES6 module的应用场景和简单的语法介绍,比较了它们之间的不同点,大家可以阅读下面的参考文章,了解更多知识。

参考文章

The difference between CommonJs and es6 module

Learn the basics of the JavaScript module system and build your own library

Relation between CommonJS, AMD and RequireJS?

es6.ruanyifeng.com/#docs/modul…