缘起
发布一个 npm UI 仓库需要生成的打包文件有 下面三个格式,有点疑惑。
原文
What Are CJS, AMD, UMD, and ESM in Javascript?
序
首先呢,javascript 本身是不能导入和导出模块的。所以早期编写 es5 类型的 js 文件很长,那时候虚拟 dom 还没有诞生或大范围使用,整个文件是一个作用域,函数之间的相互引用也在一个文件中,但这样文件会越来越长,复杂的结构但只有一个文件,这样很糟糕。
所以呢,聪明的小伙伴们开始用各种方式给 javascript 加入模块化的能力,比如 cjs、amd、umd、esm等。
下文主要总结性的从语法结构、构造目的、还有基本的表现形式介绍这几个模块化的方式。我的目标是帮助读者们更深入的认识这几个结构当你们遇到他们的时候。
CJS
CJS 是 CommonJS 的缩写,下面是它的基本结构:
// importing
const path = require("path"); // local like this: "./src/a.js"
// exporting
module.exports = function path(){}
- nodejs 就是用的这个模块化方式
- cjs 模块化方式是同步的
- 你可以引入一个 node_modules 或者本地的文件,如
const myLocalModule = require('./some/local/file.js')或者var React = require('react') - cjs 引入的是导出包对象的副本(TODO:地址引用?还是)
- cjs 不能在浏览器执行,需要被转译或者打包
AMD
AMD 是 Asynchronous Module Definition 的缩写。比如:
define(['dep1','dep2'],function(dep1,dep2){
return null;
})
// or
define(function(require){
var dep1 = require('dep1');
return null;
});
- AMD 导入模块是异步的,正如让它的名称所诠释的那样:异步模块的定义。
- AMD 是专门为了前端被提出的,因为 cjs 是后端的模块化方式
- AMD 没有 cjs 直观,我感觉完全相反。
UMD
UMD 的定义是 Universal Module Definition,它的源码有点类似这个:
(function(root,factory){
if(typeof define === 'function' && define.amd){
define(["jquery","underscore"],factory);
}else if(typeof exports === "object"){
module.exports = factory(require("jeuery"),require("undeerscore"));
}else{
root.Requester = factory(root.$,root._);
}
}(this,fucntion)$,_){
// todo
return null;
})()
- 它可以在前后端通用,正如它的定义所写
- 和 cjs 或者 amd 不同的是,它更像是一个融合了多个模块配置的一个模块
- 当使用 rollup/webpack 等打包工具的时候,UMD 通常用来作为一个备选模块生成。
ESM
ESM 表示的是 ES Modules. 它是 javascript 实现标准模块化方案的提案,相信大家都用过。
import React,{useRef} from "react";
export const function a(){}
- 它可以运行浏览器端
- 它有着类似 cjs 的语法结构和 AMD 的异步特点
- 支持
tree-shaking,基于 es6 的静态导入规则 - es6 允许类似 rollup 的打包工具移除掉不必要的代码,允许网站只需要只需要加载少量的代码,以便更快的显示内容。
- 目前部分的浏览器支持在 html 使用 es6 模块
<script type="module">
import {f4} from 'my-lib';
f4()
</script>
划重点
- ESM 应该是最好的模块化方案,它有着简单的语法结构、异步加载特点、以及支持
tree-shaking - UMD 前后端都可以用到,一般作为打包后的备选方案,为了防止 ESM 没生效
- CJS 是同步的,后端专属
- AMD 是异步的,前端专属
参考
阮一峰: Javascript模块化编程(二):AMD规范
前端模块化详解