如何构建支持静态资源、Tree Shaking及TS声明的自定义NPM包
两种现有方案的核心问题
你试过的两个方案都存在硬伤,不适合做可Tree Shaking的React组件包:
- 纯
tsc编译方案:tsc仅处理TS/TSX语法转译,不会处理静态资源、样式文件的拷贝与路径重写,手动写脚本维护资源路径的成本极高,后续迭代很容易出路径错误。 - Webpack单bundle方案:哪怕你全程使用ESM语法、在消费端配置
sideEffects,只要输出为单文件index.js,就无法实现可靠的Tree Shaking——构建工具无法对单文件内的导出做安全的无用代码剔除,很容易出现全量打包的问题。
推荐实现方案(Rollup 生态)
这是目前业内React组件库的主流打包方案,可以一次性满足你所有需求,配置维护成本很低。
核心依赖安装
先安装必要的打包依赖:
npm i -D rollup @rollup/plugin-typescript @rollup/plugin-node-resolve @rollup/plugin-commonjs rollup-plugin-postcss @rollup/plugin-url tslib
各插件作用:
@rollup/plugin-typescript:对接TS编译能力,转译TS/TSX代码,可自动生成对应.d.ts类型声明文件rollup-plugin-postcss:处理css/less/sass等样式文件,支持样式抽离、CSS Modules、自动加前缀等能力@rollup/plugin-url:处理svg/png等静态资源,小体积资源自动转base64,大体积资源自动拷贝到输出目录并重写引入路径- 其余两个插件用于解析node_modules依赖、兼容CommonJS格式的第三方包
配置文件编写
首先在项目根目录创建rollup.config.js:
import typescript from '@rollup/plugin-typescript'; import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import postcss from 'rollup-plugin-postcss'; import url from '@rollup/plugin-url'; export default { input: 'src/index.ts', // 包入口,所有需要对外导出的组件都在此处统一export output: [ { dir: 'dist/esm', format: 'esm', // 输出ESM格式,是Tree Shaking生效的基础前提 sourcemap: true, preserveModules: true, // 核心配置:保留模块独立文件结构,不合并为单bundle preserveModulesRoot: 'src', } ], plugins: [ resolve({ extensions: ['.ts', '.tsx', '.js', '.jsx'] }), commonjs(), postcss({ extract: 'index.css', // 抽离独立样式文件,消费方可以单独引入 modules: false, // 项目用CSS Modules的话改为true即可 }), url({ limit: 8192, // 8KB以下资源转base64内嵌,以上资源自动拷贝到输出目录 include: ['**/*.svg', '**/*.png', '**/*.jpg', '**/*.gif'], fileName: 'assets/[name][hash][extname]', }), typescript({ tsconfig: './tsconfig.json', declaration: true, // 自动生成.d.ts类型声明文件 declarationDir: 'dist/esm', outDir: 'dist/esm', }) ], external: ['react', 'react-dom'] // 将react、react-dom标记为外部依赖,不打包进产物,避免重复引入 }
然后修改package.json的关键字段,这是Tree Shaking、类型声明、包入口正确生效的核心:
{ "main": "dist/esm/index.js", "module": "dist/esm/index.js", "types": "dist/esm/index.d.ts", "sideEffects": [ "*.css", "*.less", "*.scss" ], "files": ["dist"], "scripts": { "build": "rollup -c" } }
注意tsconfig.json需要做几个基础配置:compilerOptions中module设为ESNext、jsx根据React版本设置(React17+设为react-jsx,老版本设为react)、moduleResolution设为node,避免编译出错。
针对你疑问的明确说明
- Tree Shaking 生效条件:你之前的认知不完整,仅使用ESM语法+配置
sideEffects不够,必须保留模块的独立文件结构、不能输出单bundle。按上述配置输出和源码结构对应的独立模块文件后,不管消费方用Webpack、Vite还是Rollup构建,都能正常做静态分析,只打包实际引入的组件代码。另外注意不要直接把sideEffects设为false,否则构建工具会误删有副作用的样式文件,把样式后缀加入数组即可。 - 静态资源与样式问题:上述配置中的
@rollup/plugin-url和rollup-plugin-postcss会自动处理资源拷贝、路径重写、样式抽离,不需要手动写脚本迁移文件,编译后所有引入路径都是正确的。 - JS代码压缩问题:组件库打包阶段不需要做JS压缩。压缩是应用构建层的优化项,应该由包的消费方在构建生产包时统一处理。如果在库打包阶段提前压缩代码,会破坏代码静态结构,降低Tree Shaking准确率,还会导致消费端无法获取正确的sourcemap做错误定位。
可选兼容配置
如果需要兼容还在使用CommonJS规范的旧项目,可以在output数组中新增一份CJS格式的输出配置,对应修改package.json的main字段指向CJS产物入口即可,不会影响ESM格式的Tree Shaking能力。
内容的提问来源于stack exchange,提问作者Marcin
相关产品推荐
相关产品推荐

