ES Module概念与使用解析


前言

ES Module,即 ES6 引入的 js 模块化规范,简称 ESM(下文将会使用此简称)。ESM 的设计思想是尽可能的静态化,使得编译时就能确定模块的依赖关系,完成模块加载,效率比运行时加载的 CommonJS 的加载方式要高。

运行机制

JS 引擎在对脚本静态分析时,遇到模块加载命令 import 时,会生成一个只读引用。当脚本真正运行时,再根据这个只读引用,向对应模块中取值。因此 ESM 是动态引用,并且不会缓存相关值。

编译时加载

在 ESM 中,import 命令导入时可以指定加载模块中的某个值,而不用加载整个模块。

1
import { readFileSync } from "fs";

这段代码的实质是从 fs 中加载 readFileSync 这个方法,其他的不会加载。

特性

  • 在 html 文件中,使用 script 引入 ESM(ES Module),同时设置 type=module 后,这个模块将会被被标识为顶级模块。
  • ESM 自动采用严格模式,不管有没有在模块头部添加 use strict

相关命令

ESM 提供了如下两个命令:

  • export:用于规定模块的对外接口
  • import:用于输入其他模块提供的功能

export

export 为导出命令,存在按需导出与默认导出两种导出方式,两种导出方式可以同时存在。

按需导出

即直接使用 export 命令,一般情况下,export 输出的变量就是变量原有的名字

1
2
3
4
5
6
7
8
9
// 方式一
export const name = "asuka";

// 方式二
const age = "18";

export {
age
}

但也可以使用 as 关键字来进行重命名。

1
2
3
4
5
6
7
const name = "asuka";
const age = "18";

export {
name as myName,
age as myAge
}

export 语句输出的接口,与其对应的值是动态绑定关系。即通过该接口,可以实时的访问模块内部对应的值。

默认导出

使用 export default 命令,为模块指定默认导出内容,每个模块只能存在一个 export default 命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 方式一
export default "asuka";

// 方式二
const name = "asuka";
export default name;

// 方式三
export default {
name: "asuka",
age: 18
}

// 方式四
export default function getName {
return "asuka";
}

// 方式五
export default class User {}

本质上讲,export default 就是导出一个键名为 default 的变量或方法,系统允许你在导入时为其指定任何名字。

这在后续模块的整体加载中可以直观了解到。

import

import 为导入命令,存在按需导入与默认导入两种导出方式。

按需导入

使用 import 命令,加载 export 按需导出的模块。

1
import { name } from "./user.js";

也可以通过 as 关键字重命名引入的接口。

1
import { name as myName } from "./user.js";

import 命令具有提升效果,即会提升到整个模块的头部首先执行。
这种行为的本质是由于,import 是在编译阶段执行的,在代码运行之前。

如下面的写法将不会报错:

1
2
getName();
import { getName } from "./user.js";

默认导入

针对默认导入的数据,直接为其指定名称即可。

1
import user from "./user.js";

整体导入

即使用星号 (*) 将模块内的导出全部加载,并使用 as 指定一个对象,所有输出值(包括全部的按需导出默认导出)全部加载到这个对象上。

例如现在存在模块 user.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const name = "asuka";

export const age = 18;

const getName = () => {
return name;
}

export {
name as myName,
getName
}

export default 1;

使用 import 整体加载 user.js 模块,并指定为 user 对象:

1
2
3
import * as user from "./user.js";

console.log( user );

可以得到 user 对象的内容如下,可以看到,默认导出的内容被以 default 的键名被放置到了该对象上:

1
2
3
4
5
6
[Module] {
age: 18,
default: 1,
getName: [Function: getName],
myName: 'asuka'
}