前言
ES modules 是原生 JavaScript 提供的模块功能,逐渐被更多的浏览器开始支持
入门篇
先了解下基础用法。
html 文件中使用 ES modules 语法执行 js
在 script 标签中 设置 type = module,浏览器就会以 ES modules 的标准去执行 script 标签中的 JavaScript 代码。默认情况下,代码是以严格模式执行的。
<script type="module">
console.log('this is es module')
</script>
私有作用域
每一个设置 type=module 的标签内都会形成一个私有作用域。作用域之间的变量不能直接共享。
<script type="module">
var a = 1
console.log(a)
</script>
<script type="module">
console.log(a)
</script>
第 1 组 script 中的代码正常输出变量 a 的值,第 2 段会报以下错误:a is not defined。
外链 JS 文件加载
ES modules 会以 CORS 方式加载外部 js 文件。提供 js 文件的服务器需要设置 CORS 后,ES modules 的 script 标签才能正常加载文件。
<script type="module" src="https://unpkg.com/jquery@3.6.0/dist/jquery.js"></script> //第一个文件服务器配置了CORS
<script type="module" src="https://abc.com/jquery@3.6.0/dist/jquery.js"></script> //第二个没有配置
结果:第 1 组 script 标签加载成功,第 2 组加载失败。
延迟执行,作用与 defer 一致。
未设置 type = module 前,会先执行完 01_demo.js 的代码。p 标签的渲染会被阻塞。
设置 type = module 后,p 标签的渲染不会被阻塞。
<script src="./01_demo.js" type="module"></script>
<p>测试</p>
导出
导出方式
// module.js - 单独导出
export var name = "foo module";
export class A {}
export function bar() {}
// 统一导出
var name = "foo module";
class A {}
function bar() {}
export { name, A, bar }
// app.js - 导入
import { name } from "./module.js";
console.log(name);
导出重命名
const age = 25;
export { age as Age };
默认模块导出,导入时也要重命名
const age = 25;
export { age as default };
export default age;
import { default as Age } from './module.js'
import Age from './module.js'
console.log(Age)
注意事项:
1.导出时,export 后面不是字面量对象。import 后面也不是对象解构。
2.export default { name, age } 是等价于导出一个对象,export { a, b, c } 则是固定用法。
3.进行导入操作时,导入的是同一份引用,是引用关系,并非值,这一点与 common.js 不一样。在导入之后,不能对导入变量进行修改,它是一个只读的关系。
导入
1.导入语句中,需要指定完整路径,不能忽略文件后缀名(但是现有第三包,或者其也是支持不写文件后缀名)。而 common js 是支持忽略文件后缀名。
2.路径必须以 / 开头。导入路径支持使用 cdn url 外链 js 文件的形式。
3.以下导入方式等价于执行导入文件中的 js 代码。
import {} from './module.js'
import './module.js'
4.将模块中所有内容一次性导入,通过对象.变量方式访问。
import * as xxx from './module.js'
5.动态导入,import() 等价于函数调用,非函数调用形式只能置于顶层。
import('./module.js').then(module => {
console.log(module)
})
6.默认成员和具名成员导入。
import { name, age, default as title } from './module.js'
import title, { name, age } froom './module.js'
导出导入成员
一种代码组织方式,将散落模块组织成新模块。
// components/button.js
export const Button = "Button component";
// components/radio.js
export const Radio = "Radio component";
// components/index.js
import { Button } from "./button.js";
import { Radio } from "./radio.js";
export { Button, Radio };
// app.js
import { Button, Radio } from "./component/index.js";
console.log(Button);
console.log(Radio);
Polyfill 篇
ES modules 是 ES6 推出的新语法特性,在低版本浏览器上需要通过 Polyfill 方式去解决兼容性问题.
script 添加 nomodule 设置,避免在正常浏览器出现代码执行两次的情况。nomodule 表示当前浏览器不支持 modules 的情况下加载使用。
<script nomodule src="https://unpkg.com/promise-polyfill@8.2.0/dist/polyfill.min.js"></script>
<script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/babel-browser-build.js"></script>
<script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/browser-es-module-loader.js"></script>
<script type="module">
import { foo } from './module.js'
console.log(foo)
</script>
Node 中应用篇
背景
Node 中对 ES modules 语法特性的支持还在实验阶段。node 版本大于 8.5,脚本执行命令添加 --experimental-modules 即可运行 ES modules 代码。
Node 对 ES modules 特性支持的源码
与 CommonJS 交互
1.ES modules 中可以导入 CommonJS 模块。
2.CommonJS 中不能导入 ES modules 模块。
3.CommonJS 始终只会导出一个默认成员。
4.import 不是解构对象