Webpack splitChunks代码去重失效:Lerna多包第三方库重复打包
最近碰到个头疼的问题:我有个Lerna项目,里面包含两个结构完全一致的包p1和p2,两者都引入了第三方依赖eosjs@beta(体积约50KB)。为了测试打包表现,我用npx create-react-app sizetest --typescript创建了一个React示例项目,结果发现:
- 未引入任何子包时,打包后的核心chunk大小是117K:
ls examples/sizetest/build/static/js/ -lah 117K 1.13eeb203.chunk.js 1.4K main.2170ea98.chunk.js 1.5K runtime~main.229c360f.js - 只引入p1后,核心chunk涨到165K,刚好增加了50KB,符合预期;
- 但同时引入p1和p2后,核心chunk直接跳到212K,又额外增加了50KB——等于
eosjs被重复打包了两次,完全没实现依赖复用!
补充下背景:p1和p2是用yo node-typescript-webpack生成的,我已经尝试过参考谷歌文档添加splitChunks配置(对应仓库里webpack.config.js的196-211行),虽然生成了commons文件,但用grep检查后发现eosjs的代码还是重复出现在打包产物里。
问题根源&解决方案
折腾了一阵后,发现问题主要出在Lerna的依赖管理和webpack的模块解析策略上,下面是几个有效的解决方向:
1. 开启Lerna的依赖hoist模式
Lerna默认可能不会把第三方依赖提升到根目录,导致p1和p2各自的node_modules里都有一份eosjs。可以在项目根目录的lerna.json里开启hoist:
{ "npmClient": "npm", "useWorkspaces": true, "hoist": true, "hoistExclude": [] }
然后执行lerna bootstrap --hoist重新安装依赖,这样eosjs@beta会被统一放在根目录的node_modules里,让两个子包共享同一版本的依赖。
2. 调整webpack的splitChunks配置,强制复用已有模块
之前的splitChunks配置可能没正确捕获来自Lerna子包的第三方依赖,修改配置如下,重点确保reuseExistingChunk: true开启,它会让webpack优先复用已经打包过的模块,避免重复打包:
splitChunks: { chunks: 'all', cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'all', priority: -10, enforce: true }, commons: { name: 'commons', minChunks: 2, chunks: 'all', priority: -20, reuseExistingChunk: true } } }
3. 把子包的依赖设为外部依赖,避免子包自行打包
p1和p2的webpack配置可能默认把eosjs打包进了自身的输出文件里,导致主项目引入时重复打包。可以在子包的webpack配置里添加externals配置,把eosjs标记为外部依赖:
externals: { 'eosjs': 'eosjs' }
这样子包不会把eosjs打包进去,而是由主项目(React示例项目)统一处理这个依赖,自然就能实现复用了。
4. 验证结果
修改完配置后重新构建,然后用source-map-explorer分析打包产物,确认eosjs只出现一次:
npx source-map-explorer examples/sizetest/build/static/js/*.chunk.js
按照这几步操作后,应该就能解决重复打包的问题,让p1和p2共享同一份eosjs的打包代码了。
内容的提问来源于stack exchange,提问作者Warrick FitzGerald

