Search K
Appearance
Appearance
前端模块化是一种组织和管理代码的方式,使得代码更加模块化、可维护和可重用。以下是几种常见的前端模块化方式,包括 CommonJS
、ESM
(ECMAScript Modules
)、IIFE
(Immediately Invoked Function Expression
)等。
ESM 是 ECMAScript 标准的一部分,用于模块化 JavaScript 代码。它是现代前端开发中最推荐的模块化标准,得到了所有主流浏览器和 Node.js 的支持。
模块导出:
// math.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
模块导入:
// app.js
import { add, subtract } from './math.js';
console.log(add(1, 2)); // 输出: 3
console.log(subtract(3, 1)); // 输出: 2
CommonJS 是一种用于服务器端 JavaScript 的模块化标准,主要用于 Node.js 环境。它通过同步方式加载模块。
模块导出:
// math.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
module.exports = {
add,
subtract
};
模块导入:
// app.js
const math = require('./math');
console.log(math.add(1, 2)); // 输出: 3
console.log(math.subtract(3, 1)); // 输出: 2
IIFE 是一种立即执行函数表达式,用于创建独立的作用域,避免全局变量污染。虽然 IIFE 不是模块化标准,但它可以用来模拟模块化行为。
// math.js
(function(exports) {
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
exports.add = add;
exports.subtract = subtract;
})(this.math = {});
模块导入:
// app.js
console.log(math.add(1, 2)); // 输出: 3
console.log(math.subtract(3, 1)); // 输出: 2
AMD 规范主要用于浏览器环境,特别是在需要异步加载模块的场景中。AMD 规范的核心思想是通过异步方式加载模块,从而避免阻塞浏览器主线程。
AMD 规范通常与模块加载器(如 RequireJS)一起使用。
模块导出:
// math.js
define([], function() {
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
return {
add: add,
subtract: subtract
};
});
模块导入:
// app.js
require(['math'], function(math) {
console.log(math.add(1, 2)); // 输出: 3
console.log(math.subtract(3, 1)); // 输出: 2
});
UMD 规范是一种通用模块定义格式,旨在兼容多种模块化标准,包括 CommonJS、AMD 和全局变量。UMD 模块可以在多种环境中使用,包括 Node.js 和浏览器。
UMD 模块通常通过一个立即执行函数表达式(IIFE)来定义,根据不同的环境选择不同的模块加载方式。
// math.js
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// CommonJS
module.exports = factory();
} else {
// Browser globals
root.math = factory();
}
}(this, function() {
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
return {
add: add,
subtract: subtract
};
}));
Node.js 环境:
// app.js
const math = require('./math');
console.log(math.add(1, 2)); // 输出: 3
console.log(math.subtract(3, 1)); // 输出: 2
AMD 环境:
// app.js
require(['math'], function(math) {
console.log(math.add(1, 2)); // 输出: 3
console.log(math.subtract(3, 1)); // 输出: 2
});
浏览器全局变量环境:
<!DOCTYPE html>
<html>
<head>
<title>UMD Example</title>
</head>
<body>
<script src="math.js"></script>
<script>
console.log(math.add(1, 2)); // 输出: 3
console.log(math.subtract(3, 1)); // 输出: 2
</script>
</body>
</html>
ESM(ECMAScript Modules):
CommonJS:
IIFE(Immediately Invoked Function Expression):
AMD:
UMD:
import
关键字。import
语句,确定模块的依赖关系。// moduleA.js
export const message = 'Hello, World!';
// main.js
import { message } from './moduleA';
console.log(message); // 输出: Hello, World!
require
关键字(CommonJS)或 import()
函数(ES6 模块)。// moduleA.js
module.exports = {
message: 'Hello, World!'
};
// main.js
const moduleA = require('./moduleA');
console.log(moduleA.message); // 输出: Hello, World!
require
) 在 CommonJS 模块系统中,module.exports
导出的是对象引用,但导出的基本类型(如字符串、数字、布尔值)是值的拷贝。这意味着:
// moduleA.js
module.exports = {
message: 'Hello, World!'
};
// main.js
const moduleA = require('./moduleA');
console.log(moduleA.message); // 输出: Hello, World!
moduleA.message = 'Goodbye, World!';
console.log(moduleA.message); // 输出: Goodbye, World!
// 在另一个文件中再次导入 moduleA
// moduleB.js
const moduleA = require('./moduleA');
console.log(moduleA.message); // 输出: Goodbye, World!
在这个例子中,moduleA
导出的是一个对象引用。当你在 main.js
中修改 moduleA.message
时,这些修改会影响到所有导入了 moduleA
的模块。
// moduleA.js
module.exports.message = 'Hello, World!';
// main.js
const { message } = require('./moduleA');
console.log(message); // 输出: Hello, World!
message = 'Goodbye, World!';
console.log(message); // 输出: Goodbye, World!
// 在另一个文件中再次导入 moduleA
// moduleB.js
const { message } = require('./moduleA');
console.log(message); // 输出: Hello, World!
在这个例子中,message
是一个基本类型的值(字符串)。当你在 main.js
中修改 message
时,这只是修改了局部变量 message
,不会影响到其他导入了 moduleA
的模块。
import
) 在 ES6 模块系统中,导出的是对象引用,无论是对象还是基本类型。这意味着:
// moduleA.js
export const message = 'Hello, World!';
// main.js
import { message } from './moduleA';
console.log(message); // 输出: Hello, World!
message = 'Goodbye, World!'; // 报错,因为 message 是只读的
在这个例子中,message
是一个基本类型的值(字符串)。直接修改 message
会报错,因为导入的变量是只读的。
// moduleA.js
export const message = 'Hello, World!';
// main.js
import * as moduleA from './moduleA';
console.log(moduleA.message); // 输出: Hello, World!
moduleA.message = 'Goodbye, World!'; // 修改成功
console.log(moduleA.message); // 输出: Goodbye, World!
// 在另一个文件中再次导入 moduleA
// moduleB.js
import { message } from './moduleA';
console.log(message); // 输出: Goodbye, World!
在这个例子中,通过解构赋值,你可以修改模块的属性。
CommonJS (require
):
ES6 模块系统 (import
):