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.js和dojo都支持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代码
参考链接
- StackOverflow: Relation between CommonJS, AMD and RequireJS?
- Google Developer: Using JavaScript modules on the web
- Auth0: JavaScript Module Systems Showdown: CommonJS vs AMD vs ES2015
- Reddit: NPM vs JAM, RequireJS vs Browserify vs Ender
- JavaScript Modules Part 2: Module Bundling
- MDN: Web technology for developers JavaScript JavaScript reference Statements and declarations import
- A 10 minute primer to JavaScript modules, module formats, module loaders and module bundlers
- JavaScript Modules: A Beginner’s Guide
CommonJs
- How to require CommonJS modules in the browser?
- Getting started with Node.js modules: require, ms, imports and beyond
IIFE
ESM
- Understanding ES6 Modules
- ES6 Modules in Depth
- 7 Different Ways to Use ES Modules Today!
- A draft specification for browser module loading
NodeJS & ESM
- How to use module.ms in Node.js
- Node.js v11.1.0 Documentation
- standard-things/esm
- Tomorrow’s ES Modules Today!
Browser & ESM
- ES6 modules support lands in browsers: is it time to rethink bundling?
- Native ECMAScript modules in the browser
- Using ES Modules in the Browser Today
- ECMAScript modules in browsers
- Real World Experience with ES6 Modules in Browsers
- Using ES6 modules in the browser
- Can I Use: JavaScript modules via script tag
TypeScript & ESM
Rollup
TC-39
书籍