# 【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/exports
ESM
: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]。