# 【JavaScript进阶】从import和require到JS模块化
# 一、JavaScript模块标准
在JavaScript中,存在两种模块化标准。第一种是ES6模块化标准(以下简称ESM),另一种则是CommonJS规范(以下简称CJS)。
⚠️二者互不兼容。
其中CJS最早出现。在此之前,前端没有模块化概念,在各种插件中的变量命名容易污染全局作用域,简直就是灾难。
import/export和require/exports分属于ESM和CJS标准,它们有很大的区别。
# 1.CJS
 CJS先于ESM被提出,CJS诞生之初是为了在浏览器环境之外的地方使用模块化的JavaScript。
在
CJS中,每一个文件就是一个模块,拥有自己独立的作用域,变量,以及方法等,对其他的模块都不可见。CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。require方法用于加载模块。 —— 阮一峰
Node采用CJS规范,所以我们会发现CJS模块通常需要Node环境的支持,换句话说,它需要服务器环境的支持。
要点
- 1.
Node支持CJS和ESM,参考esm和cjs模块的相互加载。 - 2.
CJS不能在浏览器中运行,但可以将其转换成ESM后在浏览器中运行。 
# 2.ESM
 ESM是ECMAScript标准的模块化标准,在ES6中被正式提出。在此之前的CJS标准应用场景是服务端环境,ESM则是用于浏览器环境。
要点
1.
ESM加载时属于静态加载,可以在加载时进行Tree Shaking,即树摇,去除模块中引入了却未被使用的方法,以减小打包后的代码体积,获得更快的加载速度。2.
ESM可以直接在现代浏览器中运行。例👇:<script type="module"> ... </script>1
2
3
# 3.二者区别
如何简单区分二者:👇
CJS:require/exportsESM:import/export
加载时的区别:
CJS属于运行时加载(动态加载),理论上可以放在代码的任何地方,CJS在加载时会加载模块内的所有的东西,使用其中的一种或几种。而ESM属于编译时加载(静态加载),必须放在代码顶部,它可以在编译时就完成模块加载,速度比较快。
为何动态加载不能进行Tree Shaking,只有静态加载时可以?
CJS属于运行时加载,可以在代码中的任何地方引入,如果有以下代码片段:
if (flag) {
    myModule = require("a_module_path");
} else {
    myModule = require("b_module_path");
}
 2
3
4
5
由于无法确定用到了哪个模块,所以无法进行Tree Shaking。
与CJS不同的是ESM中所有模块必须在代码顶部声明引入。
import AModule from "a_module_path";
import BModule from "b_module_path";
 2
# 二、import/export和require/exports的使用
 # 1.import/export
 // module
export default func;
export const func;
export function func;
export { func1, func2 };
export * from "other_module_path";
// main
import func from "module";
import { newFunc as func } from "module";
import * as funcModule from "module";
import { func } from "module";
import module, { func } from "module";
 2
3
4
5
6
7
8
9
10
11
12
13
# 2.require/exports
 // module
module.exports = {
  func: function() {}
}
// main
const funcModule = require("module");// 全部引入
funcModule.func()
 2
3
4
5
6
7
8
# 三、ESM和CJS模块的相互加载
 ⚠️注意
值得注意的是,两种模块的相互加载需要在Node环境下,因为CJS模块不能在浏览器中运行。
# 1.在ESM中引入CJS模块
 import可以加载CJS模块,但是必须是整体加载,否则会报错。👇
// module 为CJS模块
import module from "module";// √
import { func } from "module";// ×
// 部分加载
import module from "module";
const { func } = module;
 2
3
4
5
6
7
在Node环境中使用import加载CJS模块时,Node会将CJS模块的module.exports输出的结果作为默认输出。即module.exports等于export dafult。👇
// CJS module
module.exports = {
  a: "a",
  b: "b"
}
// main
import module from "module";// module: {a: "a", b: "b"}
import { default as module } from "module";// module: {a: "a", b: "b"}
 2
3
4
5
6
7
8
9
# 2.在CJS中引入ESM模块
 // ESM module
export default {
  func() {
    console.log("fun!")
  }
}
// main
const module = require("module");// ×
// {default: {...}, ...}
const module = require("module").default;// √
 2
3
4
5
6
7
8
9
10
11
# 四、浏览器如何加载CJS
 上面一节的ESM和CJS模块的相互加载实际上讲的是如何在Node环境加载ESM,那么浏览器环境下如何加载CJS?
答案是转格式。。详见参考资料[4]。