JS模块:ESM
Eijux(讨论 | 贡献)2023年4月3日 (一) 21:14的版本 (创建页面,内容为“category:JavaScript == 关于<ref>参考列表: # [https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Modules MDN:《JavaScript 模块》] # [https://zh.javascript.info/modules-intro Javascript.info:《模块》] # [https://wangdoc.com/es6/module 网道:ES6 教程:《Module 的语法》] </ref> == 常常与 进行比较, '''ESM'''(ECMAScript Module)在 2015 年的时候出现于 ES-6 中,逐渐取代 AMD、UMD、'''CJM'''(…”)
关于[1]
常常与 进行比较, ESM(ECMAScript Module)在 2015 年的时候出现于 ES-6 中,逐渐取代 AMD、UMD、CJM(CommonJS)。 参考:JS模块:ESM 与 CJM
一个模块(module)就是一个文件,模块间通过 export 和 import 互相加载。
export:导出[2]
export 语句:用于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过 import 语句使用它们。 —— 被导出的绑定值依然可以在本地进行修改。 —— 在使用 import 进行导入时,这些绑定值只能被导入模块所读取,但在 export 导出模块中对这些绑定值进行修改,所修改的值也会实时地更新。
- 导出单个接口:
// 导出变量名 export let name1, name2, …, nameN; // also var, const // 导出表达式 export let name1 = …, name2 = …, …, nameN; // also var, const // 导出方法 export function FunctionName(){...} // 导出类 export class ClassName {...}
- 导出多个接口:
export { name1, name2, …, nameN };
- 重命名导出:
export { variable1 as name1, variable2 as name2, …, nameN };
- 在导入期间,必须使用相应对象的相同名称
- 解构导出并重命名:
export const { name1, name2: bar } = o;
- 默认导出:
export default expression; export default function (…) { … } // also class, function* export default function name1(…) { … } // also class, function* export { name1 as default, … };
- 一个模块只能有一个默认导出
- 导出模块合集:
export * from …; // does not set the default export export * as name1 from …; // Draft ECMAScript® 2O21 export { name1, name2, …, nameN } from …; export { import1 as name1, import2 as name2, …, nameN } from …; export { default } from …;
import:静态导入[3]
import 语句:用于导入由另一个模块导出的绑定。 —— 无论是否声明了 strict mode,导入的模块都运行在“严格模式”下。 —— 在浏览器中,import 语句只能在声明了 type="module" 的 <script> 的标签中使用。
- 导入单个接口:
import {myExport} from '/modules/my-module.js';
- 导入多个接口:
import {foo, bar} from '/modules/my-module.js';
- 重命名导入:
import {exportName1 as name1, exportName2 as name2} from '/modules/my-module.js';
- 导入一个模块作为“副作用”:
import '/modules/my-module.js';
- 运行模块中的全局代码,但实际上不导入任何值
- 导入默认值:
// 仅导入默认值 import myDefault from '/modules/my-module.js'; // 导入默认值与其他接口 import myDefault, * as myModule from '/modules/my-module.js'; import myDefault, {foo, bar} from '/modules/my-module.js';
- 导入模块合集:
import * as myModule from '/modules/my-module.js';
- 通过该对象的属性访问模块接口
- 重导出:
export { default as function1, function2 } from 'bar.js';
- 等同于“联合使用 import、export”:
import { default as function1, function2 } from 'bar.js'; export { function1, function2 };
import():动态导入[4]
import() 方法:通常称为“动态导入”,是一个类似函数的表达式,它将返回一个 Promise —— 【常配合 async/await 使用】 —— 用于将 ECMAScript 模块异步、动态地加载到潜在的非模块环境中。 ——仅在必要时使用“动态导入”——【“静态导入”更适合于加载初始依赖项,利于静态分析工具和“tree shaking”(树抖动)】
- 导入一个模块作为“副作用”:
(async () => { if (somethingIsTrue) { const { default: myDefault, foo, bar, } = await import("/modules/my-module.js"); } })();
- 运行模块中的全局代码,但实际上不导入任何值
- 响应用户操作按需导入:
const main = document.querySelector("main"); for (const link of document.querySelectorAll("nav > a")) { link.addEventListener("click", (e) => { e.preventDefault(); import("/modules/my-module.js") .then((module) => { module.loadPageInto(main); }) .catch((err) => { main.textContent = err.message; }); }); }
- 根据环境导入不同的模块:
let myModule; if (typeof window === "undefined") { myModule = await import("module-used-on-server"); } else { myModule = await import("module-used-in-browser"); }
- 导入带有非文字说明符的模块:
Promise.all( Array.from({ length: 10 }).map((_, index) => import(`/modules/module-${index}.js`), ), ).then((modules) => modules.forEach((module) => module.load()));
尽管 import() 看起来像一个函数调用,但它只是一种特殊语法,只是恰好使用了括号(类似于 super())。 因此,不能将 import 复制到一个变量中,或者对其使用 call/apply —— 因为它不是一个函数!!!
import.meta:元数据属性[5]
import.meta是一个给 JavaScript 模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,比如说这个模块的 URL。
- import.meta.url:当前模块的 URL 路径
- import.meta.scriptElement:(浏览器特有)加载模块的那个 <script> 元素
相关用法
聚合重导出
即:在一个模块中,聚合多个模块的导出
childModule1.js 中:
let myFunction = ...; // assign something useful to myFunction let myVariable = ...; // assign something useful to myVariable export {myFunction, myVariable};
childModule2.js 中:
let myClass = ...; // assign something useful to myClass export myClass;
parentModule.js 中:聚合重导出
export { myFunction, myVariable } from 'childModule1.js'; export { myClass } from 'childModule2.js';
顶层模块中:从 parentModule.js 模块导入
import { myFunction, myVariable, myClass } from 'parentModule.js'
<script>中嵌入[6]
在浏览器中,import 语句只能在声明了 type="module"
的 script 的标签中使用。
相关属性:
- src:定义引用外部脚本的 URI,可以用来代替直接在文档中嵌入脚本。
- 指定该属性的 <script> 元素标签内不应该再有嵌入的脚本;
- type:定义 <script> 元素包含或 src 引用的脚本语言。
- 支持的 MIME 类型包括:text/javascript、text/ecmascript、application/javascript、application/ecmascript;
- 如果 type 属性为“module”,代码会被当作“JavaScript 模块”;
- async:如果存在该属性,脚本会被并行请求,并尽快解析和执行。
- 脚本执行顺序:先就绪(下载完成)的先执行;
- defer:如果存在该属性,脚本将在文档完成解析后,触发 DOMContentLoaded 事件前执行。
- 脚本执行顺序:按照定义顺序依次执行;
参考
- ↑ 参考列表:
- ↑ 参考:MDN:《export》
- ↑ 参考:MDN:《import》
- ↑ 参考:MDN:《import()》
- ↑ 参考:MDN:《import.meta》
- ↑ 参考:MSN:《<script>》