目录
- 1、认识 ES Module
- 2、ES Module基本使用
- 3、export关键字
-
- 3.1、导出方式一——直接导出
- 3.2、导出方式二——通过as起别名
- 3.3、导出方式三——定义的时候就直接导出
- 4、import关键字
-
- 4.1、导入方式一——直接导入
- 4.2、导入方式二——通过as起别名
- 4.3、导入方式三——可以给整个模块起别名
- 5、export和import结合使用
- 6、default默认导出
-
- 6.1、导出方式一——默认导出
- 6.2、导出方式二——定义时直接导出
- 7、import函数
- 8、import meta(了解)
- 9、ES Module的解析流程
-
- 9.1、阶段一:构建阶段
- 9.2、阶段二和三——实例化阶段-求值阶段
1、认识 ES Module
- JavaScript没有模块化一直是它的痛点,所以才会产生我们前面学习的社区规范:CommonJS、AMD、CMD等,所以在ECMA推出了自己的模块化系统时,大家也是兴奋异常。
- ES Module和CommonJS的模块化有一些不同之处:
- 一方面它使用了import和export关键字;
- 另一方面它采用编译期的静态分析,并且也加入了动态引用的方式;
- ES Module模块采用export和import关键字来实现模块化:
- export负责将模块内的内容导出;
- import负责从其他模块导入内容;
- 了解:采用ES Module将自动采用严格模式:use strict
2、ES Module基本使用
注意,export后面跟的{}不是一个对象,只是一种特定的语法。
// foo.js
const name = 'lisi'
const age = 18
function sayHello() {
console.log("sayHello");
}
// export { 标识符1, 标识符2, 标识符3 }
export {
name,
age,
sayHello
}
// main.js
// 注意事项一:在浏览器中直接使用esmodule时,必须在文件后面加上后缀名.js
import { name, age, sayHello } from './foo.js'
console.log(name);
console.log(age);
sayHello()
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--注意事项二:在我们打开对应的html时,如果html中有使用模块化的代码,那么必须开启一个服务来打开-->
<script src="./foo.js" typt="module"></script>
<script src="./main.js" type="module"></script>
</body>
</html>
说明:
- 如果直接在浏览器中运行代码,会报错:
- 这个在MDN上面有给出解释:
- 其他模块与标准脚本的不同
- 你需要注意本地测试——如果你通过本地加载 HTML 文件(比如一个 file:// 路径的文件),你将会遇到 CORS 错误,因为 JavaScript 模块安全性需要。
- 你需要通过一个服务器来测试。
- 这里使用的VSCode插件:Live Server
3、export关键字
3.1、导出方式一——直接导出
export {
name,
age,
sayHello
}
3.2、导出方式二——通过as起别名
export {
// 导出时给标识符起一个别名,不过实际开发中不怎么用这个方式
name as fname,
age,
sayHello
}
3.3、导出方式三——定义的时候就直接导出
export const name = 'lisi'
export const age = 18
export function sayHello() {
console.log("sayHello");
}
4、import关键字
4.1、导入方式一——直接导入
import { name, age, sayHello } from './foo.js'
4.2、导入方式二——通过as起别名
一般在导入的时候起别名,而不是在导出的时候
import { name as fname, age, sayHello } from './foo.js'
4.3、导入方式三——可以给整个模块起别名
import * as foo from './foo.js'
console.log(foo.name);
console.log(foo.age);
foo.sayHello()
5、export和import结合使用
这是一种开发思想。
- 补充:export和import可以结合使用
- 为什么要这样做呢?
- 在开发和封装一个功能库时,通常我们希望将暴露的所有接口放到一个文件中;
- 这样方便指定统一的接口规范,也方便阅读;
- 这个时候,我们就可以使用export 和import结合使用
- 示例代码:
工具类库:
// utils/format.js
export function formatCount() {
console.log('formatCount');
}
export function formatDate() {
console.log('formatDate');
}
// utils/parse.js
export function parseLyric() {
console.log('parseLyric');
}
工具类统一导出出口(export和import结合使用):
// utils/index.js
// 默认方式
// import {formatCount, formatDate} from './format.js'
// import {parseLyric} from 'parse.js'
// export {
// formatCount,
// formatDate,
// parseLyric
// }
// 优化一:推荐使用这种,便于阅读
// export {formatCount, formatDate} from './format.js'
// export {parseLyric} from './parse.js'
// 优化二:
export * from './format.js'
export * from './parse.js'
使用工具类中的方法:
// main.js
import * as utils from './utils/index.js'
utils.formatCount()
utils.parseLyric()
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./main.js" type="module"></script>
</body>
</html>
6、default默认导出
- 前面我们学习的导出都是有名字的导出(named exports);
- 在导出export时指定了名字;
- 在导入import时需要知道具体的名字;
- 还有一种导出叫做默认导出(default export);
- 默认导出export时可以不需要指定名字;
- 在导入时不需要使用{},并且可以自己来知道名字;
- 它也方便我们和现有的CommonJS等规范互相操作;
- 注意:在一个模块中,只能有一个默认导出(export default);
6.1、导出方式一——默认导出
-
导出一个常量
const name = ‘foo’
const age = 18export default name
-
导出一个对象
const name = ‘foo’
const age = 18export default {
fname:name,
age,
}
可以看出export default后面的{}表示导出为一个对象。
6.2、导出方式二——定义时直接导出
export default name = 'foo1'
7、import函数
-
通过import加载一个模块,是不可以把它放到逻辑代码中的,比如:
-
为什么会出现这个情况呢?
- 这是因为ES Module在被JS引擎解析时,就必须知道它的依赖关系;
- 由于这个时候js代码没有任何的运行,所以无法在进行类似于if判断的逻辑代码中,根据代码的执行情况来获取模块是否加载;
- 甚至拼接路径的写法也是错误的:因为我们必须到运行时才能确定path的值;
- 但是某些情况下,我们确确实实希望动态的来加载某一个模块:
- 如果根据不同的条件,动态来选择加载模块的路径;
- 这个时候我们需要使用import()函数来动态加载;
√ import函数返回一个Promise,可以通过then获取结果;
let flag = true
if(flag) {
import('./foo.js').then(res => {
console.log(res.name); // 后打印
})
console.log('----'); // 先打印
}
8、import meta(了解)
- import.meta是一个给 JavaScript 模块暴露特定上下文的元数据属性的对象。
- 它包含了这个模块的信息,比如说这个模块的 URL。
- 在ES11(ES2020)中新增的特性;
// {url: 'http://127.0.0.1:5500/07_ESModule/main.js', resolve: ?}
console.log(import.meta,'meta');
9、ES Module的解析流程
- ES Module是如何被浏览器解析并且让模块之间可以互相引用的呢?
- ES modules: A cartoon deep-dive
- ES Module的解析过程可以划分为三个阶段:
- 阶段一:构建(Construction),根据地址查找js文件,并且下载,将其解析成模块记录(Module Record);
- 阶段二:实例化(Instantiation),对模块记录进行实例化,并且分配内存空间,解析模块的导入和导出语句,把模块指向对应的内存地址;(备注:阶段二生成模块环境记录(类似于函数解析时生成的环境记录)。)
- 阶段三:运行(Evaluation),运行代码,计算值并且将值填充到内存地址中;