模块化
目前模块化的标准
- commonJS:node 服务端
- es6 module: 浏览器端
- amd:
- 在浏览器端实现 commonJS 规范
- 依赖前置
- 使用 require.js,来定义加载资源
- cmd:
- 在浏览器端实现 commonJS 规范,
- 依赖后置,使用 require 加载执行
- 使用 sea.js
- umd:
- 支持在 node 端或浏览器端
- 判断在 node 端使用 commonJS 规范
- 浏览器端使用 amd 规范来加载资源
cjs (commonjs)
commonjs 是 Node 中的模块规范,通过 require 及 exports 进行导入导出 (进一步延伸的话,module.exports 属于 commonjs2)
同时,webpack 也对 cjs 模块得以解析,因此 cjs 模块可以运行在 node 环境及 webpack 环境下的,但不能在浏览器中直接使用。但如果你写前端项目在 webpack 中,也可以理解为它在浏览器和 Node 都支持。
比如,著名的全球下载量前十 10 的模块 ms (opens new window)只支持 commonjs,但并不影响它在前端项目中使用(通过 webpack),但是你想通过 cdn 的方式直接在浏览器中引入,估计就会出问题了。
// sum.js
exports.sum = (x, y) => x + y;
// index.js
const { sum } = require("./sum.js");
由于 cjs 为动态加载,可直接 require 一个变量
require(`./${a}`);
esm (es module)
esm 是 tc39 对于 ESMAScript 的模块话规范,正因是语言层规范,因此在 Node 及 浏览器中均会支持。
它使用 import/export 进行模块导入导出.
// sum.js
export const sum = (x, y) => x + y;
// index.js
import { sum } from "./sum";
esm 为静态导入,正因如此,可在编译期进行 Tree Shaking,减少 js 体积。
如果需要动态导入,tc39 为动态加载模块定义了 API: import(module) 。可将以下代码粘贴到控制台执行
const ms = await import("https://cdn.skypack.dev/ms@latest");
ms.default(1000);
esm 是未来的趋势,目前一些 CDN 厂商,前端构建工具均致力于 cjs 模块向 esm 的转化,比如 skypack、 snowpack、vite 等。
目前,在浏览器与 node.js 中均原生支持 esm。
- cjs 模块输出的是一个值的拷贝,esm 输出的是值的引用
- cjs 模块是运行时加载,esm 是编译时加载
示例: array-uniq
umd
一种兼容 cjs 与 amd 的模块,既可以在 node/webpack 环境中被 require 引用,也可以在浏览器中直接用 CDN 被 script.src 引入。
(function (root, factory) {
if (typeof define === "function" && define.amd) {
// AMD
define(["jquery"], factory);
} else if (typeof exports === "object") {
// CommonJS
module.exports = factory(require("jquery"));
} else {
// 全局变量
root.returnExports = factory(root.jQuery);
}
})(this, function ($) {
// ...
});
例子
/** AMD写法 **/
define(["a", "b", "c", "d", "e", "f"], function (a, b, c, d, e, f) {
// 等于在最前面声明并初始化了要用到的所有模块
a.doSomething();
if (false) {
// 即便没用到某个模块 b,但 b 还是提前执行了
b.doSomething();
}
});
/** CMD写法 **/
define(function (require, exports, module) {
var a = require("./a"); //在需要时申明
a.doSomething();
if (false) {
var b = require("./b");
b.doSomething();
}
});
/** sea.js **/
// 定义模块 math.js
define(function (require, exports, module) {
var $ = require("jquery.js");
var add = function (a, b) {
return a + b;
};
exports.add = add;
});
// 加载模块
seajs.use(["math.js"], function (math) {
var sum = math.add(1 + 2);
});
commonjs 与 es6 模块差异
1.commonjs 输出的是值的拷贝,es6 模块输出的是值的引用
- 参考阮一峰日志
- 因为 commonjs 是运行时输出
- commonjs 模块在 require 时,会将其封装成一个函数,函数提供
module
,require
等参数 - 将 module 的,属性传入函数中,执行 commonjs 函数。获取导出内容
- commonjs 模块在 require 时,会将其封装成一个函数,函数提供
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
counter: counter,
incCounter: incCounter,
};
// test.js
var mod = require("./lib");
console.log(mod.counter); // 3
mod.incCounter();
console.log(mod.counter); // 3
// ===
function require() {
var counter = 3;
function incCounter() {
counter++;
}
return {
counter: counter,
incCounter: incCounter,
};
}
console.log(mod.counter); // 3
mod.incCounter();
console.log(mod.counter); // 3