如何编写兼容所有JavaScript运行环境与模块系统的npm包
跨端JS工具库打包发布指南
核心前置要求
你开发的是无依赖纯ES6工具库,要兼容前后端,核心是需要输出3种标准产物适配不同使用场景:
- CommonJS 格式:适配Node.js后端环境
- ES Module 格式:适配现代前端构建工具(Webpack、Vite等),支持tree shaking减包体积
- UMD 格式:适配浏览器直接通过
script标签引入的原生JS场景
Bower目前已经被社区淘汰,不需要投入时间学习。以下分别给你Rollup(更适合库打包的首选方案)和你熟悉的Webpack两种实现路径。
第一步:公共基础配置
package.json 字段配置
在项目的package.json中补充以下字段,让不同环境能正确识别对应产物:
{ "main": "dist/index.cjs.js", "module": "dist/index.esm.js", "browser": "dist/index.umd.min.js", "sideEffects": false, "scripts": { "build": "填写对应打包工具的命令,后续配置完替换" } }
sideEffects设为false是告知构建工具你的库无副作用,可以安全做tree shaking。
Babel 配置
不管用哪种打包工具都需要配置Babel做语法兼容:
- 安装依赖:
npm install @babel/core @babel/preset-env -D - 项目根目录新建
babel.config.json:
{ "presets": [ ["@babel/preset-env", { "targets": "> 0.25%, not dead, node 12", "modules": false }] ] }
modules设为false是为了保留ES模块语法,避免tree shaking失效。
第二步:打包工具配置(二选一即可)
方案1:Rollup(推荐,库打包产物更精简)
Rollup是专门为JS库设计的打包工具,零配置也能生成干净的产物,学习成本很低。
- 安装依赖:
npm install rollup @rollup/plugin-babel @rollup/plugin-terser @rollup/plugin-node-resolve -D - 根目录新建
rollup.config.js:
import babel from '@rollup/plugin-babel'; import terser from '@rollup/plugin-terser'; import resolve from '@rollup/plugin-node-resolve'; export default { input: 'src/index.js', // 替换为你的库入口文件路径 output: [ // 输出CommonJS产物 { file: 'dist/index.cjs.js', format: 'cjs', exports: 'auto' }, // 输出ES Module产物 { file: 'dist/index.esm.js', format: 'es' }, // 输出压缩后的UMD产物 { file: 'dist/index.umd.min.js', format: 'umd', name: 'MyUtils', // 替换为你的库的全局变量名,浏览器引入后会挂载到window上 plugins: [terser()] } ], plugins: [ resolve(), babel({ babelHelpers: 'bundled' }) ] };
- 把
package.json里的build脚本替换为"build": "rollup -c",运行npm run build即可生成所有产物。
方案2:Webpack(你已熟悉的工具)
如果你不想学习新工具,用Webpack也可以实现,只是产物体积会比Rollup稍大:
- 安装依赖:
npm install webpack webpack-cli babel-loader terser-webpack-plugin -D - 根目录新建
webpack.config.js:
const TerserPlugin = require('terser-webpack-plugin'); const baseConfig = { entry: './src/index.js', // 替换为你的库入口文件路径 module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: 'babel-loader' } ] }, optimization: { minimizer: [new TerserPlugin()] } }; // 导出多份配置同时生成三种产物 module.exports = [ // CommonJS产物 { ...baseConfig, output: { filename: 'index.cjs.js', path: __dirname + '/dist', library: { type: 'commonjs2' } } }, // ES Module产物 { ...baseConfig, experiments: { outputModule: true }, output: { filename: 'index.esm.js', path: __dirname + '/dist', library: { type: 'module' } } }, // UMD产物 { ...baseConfig, output: { filename: 'index.umd.min.js', path: __dirname + '/dist', library: { name: 'MyUtils', // 替换为你的库的全局变量名 type: 'umd' } } } ];
- 把
package.json里的build脚本替换为"build": "webpack --mode production",运行npm run build即可生成所有产物。
第三步:发布到npm的流程
- 注册npm账号,本地运行
npm login按照提示登录账号 - 确认
package.json的name字段没有和npm现有包重名,version字段符合语义化版本规范,每次发布新版本号要高于上一版 - 根目录新建
.npmignore文件,排除不需要发布的内容:
src/ *.config.js babel.config.json node_modules/ .gitignore
- 运行
npm publish即可完成发布,其他开发者就可以通过npm install 你的包名安装使用。
兼容性验证建议
发布前可以做简单测试确保所有场景兼容:
- Node端测试:新建空Node项目,安装你的包后用
require引入调用功能验证 - 前端构建场景测试:新建Vite/React项目,用
import引入你的包调用功能验证 - 原生浏览器场景测试:新建HTML文件,用
script标签引入UMD产物,通过全局变量调用功能验证
内容的提问来源于stack exchange,提问作者h-sifat
相关产品推荐
相关产品推荐

