01-Module模块
# 初识 Module
什么是模块?模块是一个一个的局部作用域的代码块。
模块系统解决的主要问题:
- 模块化的问题
- 消除全局变量
- 管理加载顺序
我们用一个例子解释模块的这些作用。没有模块化的时候,我们将所有的逻辑都写在一个 js 文件中,然后在 html 中引入该 js 文件。这种方式如果 js 文件可能会很大,阅读和后期开发都很不方便。
let privateNum = 0;
// 父类
class BaseSlider {}
// 子类
class Slider extends BaseSlider {}
// 实例化子类,调用相关逻辑...
const slider = new Slider();
2
3
4
5
6
7
8
9
10
下面我们通过一步步改进来理解模块的作用。
(1) js 拆分
为了解决上述问题,我们可以将 js 拆成多份,分别引入到 html 文件中。
base.js
let privateNum = 0;
class BaseSlider {}
2
3
slider.js
class Slider extends BaseSlider {}
index.js
const slider = new Slider();
index.html
<script src="./base.js"></script>
<script src="./slider.js"></script>
<script src="./index.js"></script>
2
3
这么做实现了初步模块化,但是拆分的 js 文件之间有相互依赖关系,必须按顺序引入 html 中。并且在 base.js 中定义的 privateNum 是一个全局变量,我们希望消除这个全局变量,只在 base.js 内部使用它。
(2) 手动实现模块化
为了解决上述问题,我们可以使用学习 class 时提到的实现私有的方式来编写模块代码。
base.js
(function () {
let privateNum = 0;
class BaseSlider {}
// 暴露 BaseSlider
window.BaseSlider = BaseSlider;
})();
2
3
4
5
6
7
8
slider.js
(function () {
class Slider extends BaseSlider {}
// 暴露 BaseSlider
window.Slider = Slider;
})();
2
3
4
5
6
index.js
const slider = new Slider();
index.html
<script src="./base.js"></script>
<script src="./slider.js"></script>
<script src="./index.js"></script
2
3
这种方式解决了消除了全局变量,但是还是没有解决加载顺序的问题,并且写法复杂,可读性差。
(3) 使用 Module
下面就用到了 ES6 支持的模块。
base.js
let privateNum = 0;
class BaseSlider {}
export default BaseSlider; // 导出
2
3
4
5
slider.js
import BaseSlider from './base.js'; // 导入
class Slider extends BaseSlider {}
export default Slider; // 导出
2
3
4
5
index.js
import Slider from './slider.js'; // 导入
const slider = new Slider();
2
3
这样在 html 中就只需要引入 index.js 就可以好了,解决了模块化、全局变量、加载顺序这些问题,且可维护性好。
# Module 的导入导出
# 导入和导出
导出的东西可以被导入(import),并访问到。导出是将当前模块中的内容暴露出去,以供外部使用;导入就是将其他模块暴露出的内容导入,之后就可以访问这些内容了。
一个模块没有导出,也可以将其导入。被导入的代码会且仅会执行一次。
如:编写模块 module.js,并且没有导出任何内容。
const gender = 'male';
console.log(gender);
2
在 html 中导入 module.js。
<body>
<!-- html 中导入模块要指定 type -->
<script type="module">
// 即使导入多次,也只会执行一次 module.js 中的代码
import './module.js';
import './module.js';
</script>
</body>
2
3
4
5
6
7
8
对于导出而言,有两种方式:① export default;② export。
# export default
对于 export default
而言:
- 一个模块只能有一个
export default
- 使用导入,导入时可以随意命名
- 可以直接导出匿名内容
export default
和对应的 import
方式:
const gender = 'male';
console.log(gender);
export default gender; // 只能导出一个
2
3
4
// 导入可以随意命名
import sex from './module.js'; // 对应 export default
console.log(sex);
2
3
export default
可以导出多种结构的数据,并且 export default
的时候可以导出匿名的函数、类等,因为导入的时候会命名。
const age = 18;
const sex = 'male';
// 导出已经定义的变量
export default age;
// export default sex;
// 导出基本数据类型
// export default 20;
// 导出对象
// export default {};
// 导出函数
// const fn = () => {};
// export default fn;
// 导出匿名函数
// export default function () {}
// 导出匿名类
// export default class {}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# export
(1) 基本用法
export
导出的必须是声明,或先声明再用花括号导出。并且可以多次导出,也可以一次导出多个。
const age = 18;
const sex = 'male';
const name = 'Alex';
// 导出声明
export const hobby = 'play';
export function myFunction() {}
export class MyClass {}
// 花括号导出
export { name };
// 导出多个
export { age, sex };
2
3
4
5
6
7
8
9
10
11
12
13
14
导入指定名称的内容,可以导入多个。
import { name } from './module.js'; // 对应 export
import { age, sex, hobby } from './module.js'; // 导入多个
2
3
对于 export
导出的内容,导入时名称要对应上,所以不能直接导出匿名的内容。
const sex = 'male';
function fn() {}
// 以下导出方式都是错的
export 18; // ×
export sex; // ×
export fn; // ×
export function () {} // 匿名不行
export class {} // 匿名不行
2
3
4
5
6
7
8
9
(2) 起别名
export
方式导入导出名称必须对应上,但是可以在导出和导入的时候起别名。
导出时可以起别名:
const name = 'Alex';
const sex = 'male';
const aa = 'aa';
// 导出时可以起别名
export { name as username, sex, aa as bb};
2
3
4
5
6
导入时间也可以起别名:
import { username, sex as gender, bb as cc };
console.log(username, gender, cc);
2
3
(3) 整体导入
如果导出的内容非常多,可以使用整体导入。整体导入不仅可以导入 export
导出的内容,还可以导入 export default
导出的内容。
模块可以同时使用
export
和export default
,只不过export default
只能有一个。
const name = 'Alex';
const sex = 'male';
export { name, sex };
export default 18;
2
3
4
5
使用整体导入,用一个对象来接收所有导出的内容,其中 export default
导出的内容会放在 default
属性中。
import * as obj from './module.js';
console.log(obj.name); // 'Alex'
console.log(obj.sex); // 'male'
console.log(obj.default); // 18(export default 导出的内容)
2
3
4
5
(4) 同时导入
同时导入 export
和 export default
导出的内容(不适用整体导入)。
const name = 'Alex';
const sex = 'male';
export { name, sex };
export default 18;
2
3
4
5
可以分别写 export
和 export default
对应的导入语句。
import { name, sex } from './module.js'; // 对应 export
import age from './module.js'; // 对用 export default
2
还可以用如下方式同时导入,一定是 export default 的在前。
// 一定是 export default 的在前
import age, { name, sex } from './module.js';
2