kangyana / daily-question

When your heart is set on something, you get closer to your goal with each passing day.
https://www.webpack.top
MIT License
3 stars 0 forks source link

【Q103】打包时 cjs、es、umd 模块有何不同 #103

Open kangyana opened 1 year ago

kangyana commented 1 year ago

1. cjs (commonjs)

commonjs 是 Node 中的模块规范,通过 requireexports 进行导入导出 。 (进一步延伸的话,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}`);

2. 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 的转化,比如 skypacksnowpackvite 等。

目前,在浏览器与 node.js 中均原生支持 esm

cjs 模块输出的是一个值的拷贝,esm 输出的是值的引用。 cjs 模块是运行时加载,esm 是编译时加载。

示例: array-uniq

3. umd(Universal Module Definition)

一种兼容 cjsamd 的模块,既可以在 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 ($) {
  // ...
});

示例: react-table (opens new window), antd

这三种模块方案大致如此,部分 npm package 也会同时打包出 commonjs/esm/umd 三种模块化格式,供不同需求的业务使用,比如 antd (opens new window)

antd 的 cjs(opens new window) antd 的 umd(opens new window) antd 的 esm