1. 前言

落魄前端打字员小李下班后,觉得有点饿,于是买了一份炒粉,分量实在太足,吃不完决定打包:
小李:
老板,麻烦打包
老板:
好的,你要哪种包装盒,有 UMD, CMD, AMD, ES modules 等等,由本店金牌打包员 webpack, babel, rollup 为你服务
小李:
…
好吧,有点尬,今天主要想说下前端普遍的打包方式
2. yarn add xx 之后都下载了啥
以 组件库
antd为例子
1 | yarn add antd |
下载下来目录结构长这样子

抛去俄罗斯套娃的 node_modules 外,可以看到有三个目录,他们分别对应主流的三个模块规范 (AMD,CMD 远古模块规范现在基本没人使用了)
| 目录 | 模块规范 | 含义 |
|---|---|---|
| dist | UMD | 兼容 AMD,CMD 的模块,可以直接在浏览器中使用 |
| lib | CommonJS | 一个文件即一个模块,Node.js 采用的就是这个规范 |
| es | ES modules | ES6 模块规范 |
3. 模块的使用方式
UMD
此时,antd 变成了浏览器的全局变量,可以直接使用,好处是无需构建,直接在浏览器使用,缺点是不好维护,无法按需加载
1 | <script src="https://unpkg.com/antd"/> |
ES modules
推荐的方式,由于 import 的 module 都是静态的 , rollup 和 webpack 可以很好的去除无用代码,也就是传说中的 tree shaking , 确定就是第三方包质量参吃不齐,很多没有提供 es 的代码包
1 | import { Button } from 'antd'; |
CommonJS
缺点是 可以 require 一个 动态的 module, 所以打包工具不能 tree shaking
1 | const { Button } = require('antd'); |
4. 模块的构建
上面说了不同模块规范的使用方式,那么怎样发布 npm 的 时候 打包不同规范的模块呢?前端什么都缺,就是不缺轮子,这里主要讲解 babel , webpack 和 tsc 这三种方式
使用 webpack 打包 UMD
1 | const fs = require('fs'); |
这里的重点 主要是 output.libraryTarget 和 externals , 其他的都是比较常规的配置,如果代码是 js 编写的,替换对应的 loader 即可

使用 babel 打包
编写 .babelrc
1 | const env = process.env.BABEL_ENV || process.env.NODE_ENV; |
这里的 modules 可选项为 “amd” | “umd” | “systemjs” | “commonjs” | “cjs” | “auto” | false
如果代码是 js 编写的,在命令行执行如下命令
1 | // umd `-d` 是输出到指定目录 |
直接 改变环境变量 OUTPUT_MODULE 切换不同的输出格式即可,可以看到,在输入 ES modules 的时候,没有指定环境变量,相当于是 modules: false , 因为我们开发的时候,就是采用的 这种规范,所以不需要做额外的转换,原样输出到 es 目录即可
如果代码是 ts 编写的,问题也不大,加上 ts 的 presets 即可
1 | const env = process.env.BABEL_ENV || process.env.NODE_ENV; |
使用 tsc 打包
tsc 是 TypeScript 提供的 打包工具
首先编写 tsconfig.json
1 | { |
指定不同的模块规范,和 babel 类似,不要忘记 --declaration , 也就是输出 types 定义,这样别人使用你的包的时候,才有友好的提示
1 | // umd |
将打包脚本集成到 package.json
为了方便的进行打包,我们可以将写好的打包脚本集成到 npm scripts 里面,最后再配置 npm 的钩子 prepublishOnly 在每次发布前自动打包,这样不会担心代码更新没有重新构建,美滋滋!
1 | // package.json |
1 | npm publish |
5. 指定不同模块的路径
代码也打包了,也发布了,是不是就完事呢?当然不是,这会有个问题,回想下前面说到的 antd
1 | import { Button } from 'antd'; |

antd 提供了三种模块代码包,我们在使用的时候却并没有指明具体的路径,那我们到底使用的是 dist , es 还是 lib 呢?
查看 antd 代码包下面的 package.json 可以看到这几个字段:

- main 表示
Node.js默认会加载的路径 - module 表示
ES modules的路径 - unpkg 表示
umd的路径 (unpkg是一个免费的 cdn)
所以这两句代码是等价的:
1 | import { Button } from 'antd'; |
所以没有指定具体的路径的时候,默认是加载 lib 目录,也就是 cjs 模块 , 但是这样会引入全量的 antd 组件,结果这个问题也很简单,加载 es 目录 或者使用 babel-plugin-import 即可
1 | import { Button } from 'antd/es'; |
6. 让 webpack 优先加载 ES modules
如果依赖的库提供了 es 目录,我们可以手动指定,但会有些麻烦,我们需要手动查看每个库有没有提供 es 目录,幸运的是 webpack 可以帮我们处理,优先去寻找依赖的 es 目录,如果没找到,再降级为 lib 目录
1 | // webpack.config.js |
7. 结语
配置工程师还真滴不好当,现如今,还出现了 snowpack, vite 之类的打包工具,也非常值得学习,以上是我做一些开源项目学习到的一些打包方面的知识点,希望能对你有帮助。
