JavaScript 模块化规范的使用说明
Kecho

本文介绍了常用的 JavaScript 模块化规范的使用说明,包含 ESMA6、CommonJS 等其它规范。

视频链接

ESMA6 使用 import 关键字导入

CommonJS 使用 require 关键字导入

ES6 模块规范

ES6 模块规范主要用于客户端,也可用于服务端。

使用方式

服务端:

  • .js 文件改为 .mjs 文件
  • package.json 中添加定义 'type': 'module'

浏览器端:

  • 在 html 文件中使用 <script type="module" src="index.js"></script> 引入。

    直接在本地使用浏览器打开 html 文件浏览器会报错,请启动 web 服务器后通过 IP:端口 访问网页。

导出数据

导出有三种方式:

  • 分别导出
1
2
3
4
5
6
7
8
9
10
11
12
13
// student.js
export const name = 'zhangsan';
export const age = '18';
export function getTel (){
return '13333333333';
}
function getSubject (){
return ['Java', 'C++', '高等数学'];
}

// index.js
// import { name as newName, age, getTel } from './student.js';
// console.log(newName);
  • 统一导出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// student.js
const name = 'zhangsan';
const age = '18';
function getTel (){
return '13333333333';
}
function getSubject (){
return ['Java', 'C++', '高等数学'];
}

export { name, age, getTel };

// index.js
// import { name as newName, age, getTel } from './student.js';
// console.log(newName);
  • 默认导出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// student.js
const name = 'zhangsan';
const age = '18';
function getTel (){
return '13333333333';
}
function getSubject (){
return ['Java', 'C++', '高等数学'];
}

// export default {
// name: name,
// age: age,
// getTel: getTel
// };

// 可省略书写
export default { name, age, getTel };

// index.js
// import student from './student.js';
// console.log(student.name);

导入数据

除了使用相对应的导入方式外,还可以进行全部导入:

1
2
3
// index.js
import * as student from './student.js';
console.log(student.name);

也可以根据事件来进行动态导入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// student.js
const name = 'zhangsan';
const age = '18';
function getTel (){
return '13333333333';
}
function getSubject (){
return ['Java', 'C++', '高等数学'];
}

export { name, age, getTel };

// index.js
const btn = document.getElementById('btn');
btn.onclick = async() => {
const result = await import('./student.js');
console.log(result);
}

CommonJS 规范

CommonJS 规范主要用于服务端

使用方式

如果想要在浏览器端使用,可以通过 browserify 将原本的 js 文件进行翻译,再将翻译后的文件在 html 中引用。

导出数据

在 CommonJS 规范中有两种导出方式,导出的是一个普通对象,默认为 {}

  • 通过 exports
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// student.js
const name = 'zhangsan';
const age = '18';
function getTel (){
return '13333333333';
}
function getSubject (){
return ['Java', 'C++', '高等数学'];
}

exports.name = name;
exports.age = age;
exports.getTel = getTel;

// 也可以写成 module.exports.name = name;

// index.js
// const student = require('./student.js');
// console.log(student.name);
  • 通过 module.exports
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// student.js
const name = 'zhangsan';
const age = '18';
function getTel (){
return '13333333333';
}
function getSubject (){
return ['Java', 'C++', '高等数学'];
}

// module.exports = {
// name: name,
// age: age,
// getTel: getTel
// };

// 可省略书写
module.exports = { name, age, getTel };

// 不能写成 exports = { name, age, getTel };

// index.js
// const student = require('./student.js');
// console.log(student.name);

导入数据

导入时还能通过解构赋值:

1
2
3
// index.js
const { name: newName, age, getTel } = require('./student.js'); // 解构导入并重命名
console.log(newName);

也可以根据事件来进行动态导入:

1
2
3
4
5
6
// index.js
const btn = document.getElementById('btn');
btn.onclick = async() => {
const result = await require('./student.js');
console.log(result);
}

注意

注意点如下:

  1. 每个模块内部的:thisexportsmodule.exports 在初始时都指向同一个空对象。console.log(this===exports && exports === module.exports); // true
  2. 无论如何修改导出对象,最终导出的都是 module.exports 的值。
  3. exports 是对 module.exports 的初始引用,仅为了方便给导出对象添加属性,所以不能使用 exports = value 的形式导出数据,可以使用 exports.value = valuemodule.exports = value 导出数据。

思考以下代码在导入时获取到的数据是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// student.js
const name = 'zhangsan';
const age = '18';
function getTel (){
return '13333333333';
}
function getSubject (){
return ['Java', 'C++', '高等数学'];
}

exports = { a: 1 };
exports.b = 2;
module.exports.c = 3;
module.exports = { d: 4 };
1
2
3
4
// index.js
const student = require('./student.js');
console.log(student);
// 输出:{ d: 4 }

分析:

  1. exports-> {} <- module.exports
  2. exports -> { a: 1 }module.exports -> {}
  3. exports -> { a: 1, b: 2 }module.exports -> {}
  4. exports -> { a: 1, b: 2 }module.exports -> { c: 3 }
  5. exports -> { a: 1, b: 2 };没有东西指向 { c: 3 }module.exports -> { d: 4 }

导入时执行

两种规范在导入时都会执行代码。
如果有一个文件如下:

1
2
// log.js
console.log('hello world!');

导入时 console.log('hello world!') 会执行

1
2
3
4
5
// import './log.js';  // ES 规范
// const student = require('./student.js'); // CommonJS 规范

// 会输出:
// hello

数据引入问题

思考同一代码在 CommonJS 规范和 ES6 规范的执行结果:

  • CommonJS 规范
1
2
3
4
5
6
7
8
9
10
11
12
13
// data.js
let sum = 1;
function increment() {
sum += 1;
}
module.exports = { sum, increment };

// index.js
const { sum, increment } = require('./data.js');
console.log(sum);
increment();
increment();
console.log(sum);

输出:

1
data: 2
data: 3
1

  • ES6 规范
1
2
3
4
5
6
7
8
9
10
11
12
13
// data.js
let sum = 1;
function increment() {
sum += 1;
}
export { sum, increment };

// index.js
import { sum, increment } from './data.js';
console.log(sum);
increment();
increment();
console.log(sum);

输出:

1
data: 2
data: 3
3

对比两者发现,ES6 规范中执行完函数后的 sum 的值不一样了,是因为 index.jsdata.js 中的 sum 是同一片内存空间(在 CommonJS 规范中导入是将值进行了复制),为了避免这种情况应该在 data.js 中声明常量而不是变量,即改为 const sum = 1

模块化的好处

模块化能够有效解决:

  • 全局污染问题
  • 数据安全问题
  • 依赖混乱问题

在使用模块化以前:

1
2
3
4
5
6
7
8
9
// student.js
const name = 'zhangsan';
const age = '18';
function getTel (){
return '13333333333';
}
function getSubject (){
return ['Java', 'C++', '高等数学'];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="student.js"></script>
<title>Document</title>
</head>
<body>

</body>
</html>

使用模块化以后:

1
2
3
4
5
6
7
8
9
10
11
12
13
// student.js
const name = 'zhangsan';
const age = '18';
function getTel (){
return '13333333333';
}
function getSubject (){
return ['Java', 'C++', '高等数学'];
}
export default { name, age, getTel };

// index.js
import student from './student.js';
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="module" src="index.js"></script>
<title>Document</title>
</head>
<body>

</body>
</html>

1、全局污染

使用模块化以前,如果引入的多个文件有相同的变量名会产生冲突。使用模块化以后,相同的变量名都会有自己的作用域。

2、数据安全问题

使用模块化以前,此时在浏览器控制台是能够获取到 nameage 等变量的。使用模块化以后,此时在浏览器控制台是无法获取到 student.name 变量的。

3、依赖混乱问题

在使用模块化以前,直接在 html 文件中引入 js 的顺序至关重要,尤其是需要引入多个 js 文件时,特别需要关注他们的依赖关系来确定引入顺序,十分麻烦。使用模块化后,可以在各个 js 文件中显式导入需要使用到的模块,而不用关心 html 文件中引入 js 的顺序。

其它规范

AMD 规范 是 RequireJS 在推广过程中对模块定义的规范化产出。

CMD 规范 是 SeaJS 在推广过程中对模块定义的规范化产出。

 评论
评论插件加载失败
正在加载评论插件
总字数 29.4k