ES Modules是ES6标准的模块规范。
// ES Modules特点:
// 1. 编译时静态分析
// 2. 异步加载
// 3. 输出值的引用
// 4. this指向undefined
// 5. 自动采用严格模式
// 使用方式
// 浏览器:<script type="module">
// Node.js:.mjs扩展名或package.json中设置type: "module"
<!-- 浏览器中使用 -->
<script type="module">
import { name } from "./module.js";
console.log(name);
</script>
<script type="module" src="app.js"></script>
导出模块内容。
// 导出变量
export const name = "东巴文";
export const age = 25;
// 导出函数
export function greet() {
return "Hello";
}
// 导出类
export class User {
constructor(name) {
this.name = name;
}
}
// 先定义后导出
const url = "db-w.cn";
function fetch() {}
export { url, fetch };
// 重命名导出
export { url as siteUrl };
export { fetch as fetchData };
// 默认导出
export default "东巴文";
// 导出函数
export default function(name) {
return `Hello, ${name}`;
}
// 导出类
export default class User {
constructor(name) {
this.name = name;
}
}
// 导出对象
export default {
name: "东巴文",
url: "db-w.cn"
};
// 先定义后导出
const config = { debug: true };
export default config;
// 命名导出 + 默认导出
export const name = "东巴文";
export const age = 25;
export default class User {
constructor(name) {
this.name = name;
}
}
// 重新导出
export { name as userName } from "./user.js";
export * from "./utils.js";
export { default } from "./config.js";
导入模块内容。
// 导入命名导出
import { name, age } from "./module.js";
console.log(name, age);
// 重命名导入
import { name as userName } from "./module.js";
console.log(userName);
// 导入所有命名导出
import * as module from "./module.js";
console.log(module.name);
console.log(module.age);
// 导入默认导出
import User from "./module.js";
const user = new User("东巴文");
// 导入默认和命名
import User, { name, age } from "./module.js";
// 重命名默认导入
import { default as MyUser } from "./module.js";
// 动态导入返回Promise
async function loadModule() {
const module = await import("./module.js");
console.log(module.name);
}
// 条件导入
if (condition) {
import("./module.js").then(module => {
module.init();
});
}
// 按需加载
button.addEventListener("click", async () => {
const { showDialog } = await import("./dialog.js");
showDialog();
});
// 只执行模块代码,不导入任何内容
import "./polyfill.js";
import "./styles.css";
// 副作用模块
// analytics.js
console.log("分析脚本加载");
window.track = function() {};
ES Modules的特殊行为。
// import/export必须在顶层
// 编译时确定依赖关系
// 正确
import { name } from "./module.js";
export const x = 1;
// 错误
if (condition) {
import { name } from "./module.js"; // SyntaxError
}
// 动态导入例外
if (condition) {
import("./module.js").then(module => {}); // 正确
}
// counter.js
export let count = 0;
export function increment() {
count++;
}
// main.js
import { count, increment } from "./counter.js";
console.log(count); // 0
increment();
console.log(count); // 1(值引用,实时更新)
// 不能直接修改
// count = 10; // SyntaxError
// module.js
export const name = "东巴文";
export let count = 0;
// main.js
import { name, count } from "./module.js";
// 不能重新赋值
// name = "new"; // TypeError(const)
// count = 10; // TypeError(只读)
// 但可以修改对象属性
export const user = { name: "东巴文" };
import { user } from "./module.js";
user.name = "db-w.cn"; // 允许
// ES Modules自动使用严格模式
// 不需要"use strict"
// 模块中的this
console.log(this); // undefined
// 不允许未声明变量
// x = 1; // ReferenceError
// 不允许重复参数
// function foo(a, a) {} // SyntaxError
ES Modules的加载机制。
<!-- 模块脚本 -->
<script type="module" src="app.js"></script>
<!-- 内联模块 -->
<script type="module">
import { name } from "./module.js";
</script>
<!-- 特点 -->
<!-- 1. 自动延迟加载 -->
<!-- 2. 支持async -->
<!-- 3. 跨域需要CORS -->
<!-- 4. 需要服务器或本地服务器 -->
<!-- nomodule回退 -->
<script type="module" src="app.js"></script>
<script nomodule src="app-legacy.js"></script>
<!-- 预加载模块 -->
<link rel="modulepreload" href="./module.js">
<!-- 预加载依赖 -->
<link rel="modulepreload" href="./utils.js">
<link rel="modulepreload" href="./api.js">
<script type="module" src="app.js"></script>
// 方式1:使用.mjs扩展名
// module.mjs
export const name = "东巴文";
// 方式2:package.json设置type
// package.json
{
"type": "module"
}
// 然后可以使用.js
// module.js
export const name = "东巴文";
// Node.js内置模块
import fs from "fs";
import path from "path";
// node:协议
import fs from "node:fs";
处理循环依赖问题。
// a.js
import { b } from "./b.js";
export const a = "a";
console.log("a.js:", b);
// b.js
import { a } from "./a.js";
export const b = "b";
console.log("b.js:", a);
// main.js
import "./a.js";
// 输出:
// b.js: undefined(a还未导出)
// a.js: b
// 方案1:重构代码,避免循环
// 方案2:延迟访问
// a.js
import { b } from "./b.js";
export const a = "a";
export function getB() {
return b;
}
// b.js
import { getB } from "./a.js";
export const b = "b";
export function getA() {
return getB();
}
// 方案3:动态导入
// a.js
export const a = "a";
export async function getB() {
const { b } = await import("./b.js");
return b;
}
ES Modules的实际使用。
src/
├── index.js
├── modules/
│ ├── user.js
│ ├── post.js
│ └── comment.js
├── utils/
│ ├── format.js
│ └── request.js
└── config/
└── index.js
// utils/format.js
export function formatDate(date) {
return date.toLocaleDateString();
}
export function formatCurrency(amount) {
return new Intl.NumberFormat("zh-CN", {
style: "currency",
currency: "CNY"
}).format(amount);
}
// modules/user.js
import { request } from "../utils/request.js";
export class User {
constructor(data) {
Object.assign(this, data);
}
static async findById(id) {
const data = await request(`/api/users/${id}`);
return new User(data);
}
async getPosts() {
const { getPosts } = await import("./post.js");
return getPosts(this.id);
}
}
// index.js
import { User } from "./modules/user.js";
import { formatDate } from "./utils/format.js";
async function init() {
const user = await User.findById(1);
console.log(formatDate(user.createdAt));
}
init();
掌握了ES Modules后,让我们继续学习:
东巴文(db-w.cn) - 让编程学习更简单
🎯 东巴文寄语:ES Modules是JavaScript官方的模块标准,静态分析、值引用和异步加载是其核心特性。在现代前端开发中,ES Modules已成为主流选择。在 db-w.cn,我们帮你掌握现代模块化开发!