JavaScript标准委员会在2015年发布的ECMAScript6(ES6)标准。从此,JavaScript在语言级别正式支持Module系统。

ES6和其他JS模块格式

下面是来自es6-features的一个例子:

//  lib/math.js 
m function sum (x, y) { return x + y } 
m var pi = 3.141593

//  someApp.js 
import * as math from "lib/math" 
console.log("2π = " + math.sum(math.pi, math.pi))

//  otherApp.js 
import { sum, pi } from "lib/math" 
console.log("2π = " + sum(pi, pi))

我们把ES6定义的Module成为ESM(ECMAScript Module)。但是,在ESM之前,JavaScript就已经有几种Module系统了,在下面的章节一一介绍。

IIFE

IIFE (Immediately Invoked Function Expression) 是在ES6之前最简单的,也是最通用的一种JavaScript模块化方式。IIFE的原理很简单,就是在即时调用的函数中返回一个对象,这个对象就代表一个模块,这个对象的方法就代表这个模块的方法。

例子:

var greetings = (function(){

  function hi() {
    console.log("Hi!")
  }

  var m = {}

  m.hi = hi

  return m
})()

上面的IIFE返回一个对象m,包含了所有在IIFE中定义并导出的属性,然后IIFE的返回值被赋予greetings,这样就可以通过类似greetings.hi的手段来调用IIFE中定义的方法。

CommonJS

CommonJS是另一种比较常见的JavaScriptModule格式。NodeJS中的Module系统一开始就是依据CommonJS来实现的。

例子:

var lodash = require('lodash');  

module.exports = function(){  
  // ...
}

CommonJS一开始的目的是用于Server-Side编程,所以并不太适合浏览器。但是通过WebPack CommonJS或者 browserify的转化,可以让CommonJS在浏览器中使用。

AMD(Asynchronous Module Definition)

AMD和CommonJS类似,但是AMD的首要目的是用于浏览器,而且支持异步加载。require.jsdojo都支持AMD。

例子(来自于A 10 minute primer to JavaScript modules, module formats, module loaders and module bundlers ):

//Calling define with a dependency array and a factory function
define(['dep1', 'dep2'], function (dep1, dep2) {

    //Define the module value by returning a value.
    return function () {};
});

UMD(Universal Module Definition)

UMD试图融合IIFE、CommonJS和AMD,让模块化代码可以同时在浏览器和NodeJS中使用

System.register

systemjs提供了一种System.register格式,可以把ES6的模块转化为可以在ES5中使用的代码。

ESM和RollupJS

既然ES6推出了标准化ESM格式,那么以后JavaScript的模块系统就会渐渐朝标准化的ESM格式靠拢。甚至浏览器也会原生支持ESM,可以在<script>标签里面直接家在ESM模块。那么从现在开始,我们最好就是以ESM的格式来组织我们的JavaScript代码。但是问题来了,ESM还很新,很多JavaScript工具还不支持ESM,用ESM写的代码,可能某些工具或者浏览器还处理不了。这时候就需要一个模块转化系统的帮助,RollupJS就是这样一个模块转化系统。

RollupJS可以把根据ESM组织的代码,转化成以下格式:

  • IIFE
  • CommonJS
  • AMD
  • UMD
  • SystemJS

Rollup的安装和使用

Rollup的安装非常简单,使用yarn或者npm来安装:

npm install --global rollup

使用的话可以直接调用命令行:

rollup main.js --file bundle.js --format iife

或者使用一个rollup.config.js配置文件来控制rollup的执行:

module.exports = {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'iife'
  }
};

Rollup的常用插件

Rollup支持插件系统,下面是一个使用rollup-plugin-json插件的例子:

// rollup.config.js
import json from 'rollup-plugin-json';

export default {
  input: 'src/main.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  plugins: [ json() ]
};

其他常用插件有:

  • rollup-plugin-node-resolve,能够从NPM的安装目录node_modules查找依赖
  • rollup-plugin-commonjs,支持commonjs的导入
  • rollup-plugin-sourcemaps,支持sourcemaps的连环嵌套,有点像sorcery
  • rollup-plugin-buble, 使用[buble]来转译ES6代码

参考链接

CommonJs

IIFE

ESM

NodeJS & ESM

Browser & ESM

TypeScript & ESM

Rollup

TC-39

书籍