网站
引入
项目变大之后,所有代码写在一个文件里会有几个问题:文件太长不好找代码、变量名容易冲突、不知道哪个文件依赖哪个。
模块化就是把代码按功能拆分成多个文件,每个文件只负责一部分,需要的时候再引入。
正文
定义
模块(Module)是一个独立的 JavaScript 文件,通过
export导出功能,其他文件通过import引入使用。
模块的基本规则
每个模块文件有自己的作用域,内部的变量和函数不会污染全局。
只有显式 export 的内容才能被外部访问,其他都是私有的。
在 HTML 中使用模块,<script> 标签必须加 type="module":
<script type="module" src="main.js"></script>export 导出
命名导出(Named Export)
一个文件可以导出多个内容,导入时需要用 {} 指定名字。
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export const PI = 3.14159;也可以先定义,最后统一导出:
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
export { add, subtract };默认导出(Default Export)
一个文件只能有一个默认导出,导入时名字随意。
export default class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
}export default function greet(name) {
console.log(`你好,${name}`);
}import 导入
命名导入
用 {} 导入对应的命名导出。
import { add, subtract, PI } from "./math.js";
console.log(add(3, 5));
console.log(PI);导入时可以重命名:
import { add as sum } from "./math.js";
console.log(sum(1, 2));默认导入
不需要 {},名字可以随意取。
import Calculator from "./Calculator.js";
const calc = new Calculator();
console.log(calc.add(1, 2));同时导入默认和命名
import Calculator, { add, subtract } from "./Calculator.js";导入所有内容
用 * 把模块所有导出收集到一个对象里。
import * as math from "./math.js";
console.log(math.add(1, 2));
console.log(math.PI);模块的作用域
模块内部的变量对外不可见,除非 export。
const secret = "这是私有的";
export function getSecret() {
return secret;
}外部只能通过 getSecret() 访问 secret,不能直接使用。
不同模块之间的变量互不影响:
let count = 0;
export function increment() {
count = count + 1;
return count;
}这个 count 只属于这个模块,不会和其他模块的 count 冲突。
动态导入
import() 返回一个 Promise,可以在运行时按需加载模块。
async function loadModule() {
const module = await import("./math.js");
console.log(module.add(1, 2));
}
loadModule();适合用在需要时才加载的代码,比如点击按钮后加载某个功能模块。
button.addEventListener("click", async () => {
const { initChart } = await import("./chart.js");
initChart();
});实战例子
拆分数学工具
math.js:
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
export const PI = 3.14159;main.js:
import { add, multiply, PI } from "./math.js";
const result = add(10, multiply(3, PI));
console.log(result);拆分用户模块
user.js:
const users = [];
export function addUser(name, email) {
users.push({ name, email });
}
export function getUsers() {
return [...users];
}
export function findUser(name) {
return users.find(u => u.name === name);
}app.js:
import { addUser, getUsers, findUser } from "./user.js";
addUser("Tom", "[email protected]");
addUser("Jack", "[email protected]");
console.log(getUsers());
const user = findUser("Tom");
console.log(user);index.html:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>模块化示例</title>
</head>
<body>
<script type="module" src="app.js"></script>
</body>
</html>常见写法
| 写法 | 说明 |
|---|---|
export function fn() | 命名导出函数 |
export const val | 命名导出变量 |
export default | 默认导出 |
import { fn } from "./file.js" | 命名导入 |
import Name from "./file.js" | 默认导入 |
import * as all from "./file.js" | 导入所有 |
import("./file.js") | 动态导入 |
理解
模块化可以理解为给代码分房间。
每个文件是一个房间,房间里的东西默认外面看不到。只有你主动把某些东西放到门口(export),别人才能拿到。
别人要用的时候,需要明确说”我要哪个房间里的哪个东西”(import)。
这样每个房间保持独立,互不打扰,找东西的时候也知道去哪间房找。
拆分的核心原则:数据规则放一起,界面渲染放一起,交互状态放一起。
引出
模块化是 JS ES6 的重要特性之一,配合 JS 函数 和 JS 对象 可以更好地组织代码。
实际项目中经常用模块化配合 JS 异步编程 按需加载代码,提升页面性能。