webpack构建速度慢的常见原因
做前端开发的朋友肯定都遇到过这样的情况:项目跑起来npm run dev,然后就开始盯着终端发呆,等构建完成的那几分钟感觉像过了一个世纪,我之前接手一个中型React项目时就踩过这个坑,刚开始构建只要3分钟,后来随着组件和依赖越来越多,直接飙到了15分钟,每次改一行代码热更新都要等半分钟,简直让人崩溃,后来仔细排查才发现,webpack构建速度慢主要有几个“幕后黑手”。
第一个是项目体积太大,现在前端项目动不动就引入十几个甚至几十个第三方依赖,比如lodash、moment、echarts这些“大家伙”,每个依赖都自带一堆文件,webpack要把这些文件一个个解析、编译、打包,就像让一个人把整个超市的东西都搬到仓库,不累才怪,我那个项目当时就因为引入了完整的antd组件库,结果node_modules里光antd相关的文件就有好几千个,webpack扫描一遍都要花不少时间。
第二个是配置不合理,很多人用webpack都是直接复制网上的配置,根本没仔细看每个参数是干嘛的,比如loader没设置include/exclude,导致webpack去解析node_modules里的文件,这些文件本来就是编译好的,根本不需要再处理;或者plugins装了一堆,有些插件在开发环境根本用不上,比如压缩代码的TerserPlugin,开发时开着纯属浪费性能,我之前就见过有人在dev配置里还开着SplitChunksPlugin,结果每次构建都要额外计算代码分割,速度慢了一大截。

第三个是没有利用缓存,webpack其实自带缓存机制,但很多人没开启,导致每次构建都要从零开始编译所有文件,就像你写作业,每次都要重新抄一遍题目,而不是直接在之前的草稿上修改,能不慢吗?我那个15分钟的项目,后来发现就是没开cache,开启后第二次构建直接降到了8分钟,简直像换了个工具。
还有一个容易被忽略的原因是硬件和环境问题,虽然这个不是webpack本身的锅,但确实会影响体验,比如用机械硬盘的电脑,读写速度本来就慢,webpack处理大量文件时就会卡顿;或者电脑同时开着十几个应用,内存被占满,webpack只能挤牙膏似的运行,我之前用公司的老电脑开发,8G内存跑个项目,编译时Chrome都要卡到崩溃,后来换了16G内存的新电脑,明显感觉流畅多了。
优化webpack构建速度的基础配置方法
找到了慢的原因,接下来就该“对症下药”了,其实优化webpack构建速度,从基础配置入手就能解决大半问题,我自己摸索出一套“配置优化三步法”,亲测有效,新手也能轻松上手。
第一步是精简入口和输出配置,entry和output是webpack的“大门”和“出口”,配置不好就会让webpack做很多无用功,比如entry尽量不要用动态导入的方式写太多入口文件,除非有必要;output的filename可以加上contenthash,这样只有文件内容变了才会重新生成,方便缓存,我之前帮一个朋友看项目,他entry里写了5个入口,结果每次构建都要同时处理5个文件,后来改成单入口加路由懒加载,构建时间直接少了三分之一。
第二步是优化loader配置,loader是处理文件的“工人”,但不是所有“工人”都需要加班,最关键的是给loader加上include和exclude,明确告诉webpack哪些文件需要处理,哪些不用,比如babel-loader只需要处理src目录下的js文件,node_modules里的文件已经是编译好的,直接exclude掉就行,我在项目里是这么配的:module: { rules: [{ test: /\.js$/, loader: 'babel-loader', include: path.resolve(__dirname, 'src'), exclude: /node_modules/ }] },就这一个小改动,构建速度快了20%。
第三步是精简plugins,plugins就像给webpack“加buff”,但buff加太多反而会“过载”,开发环境下,像HtmlWebpackPlugin这种生成html的插件是必须的,但像MiniCssExtractPlugin(提取css成文件)、TerserPlugin(压缩js)、OptimizeCssAssetsPlugin(压缩css)这些生产环境才需要的插件,就别在dev配置里启用了,我之前图省事,dev和prod用同一个plugins数组,结果开发时每次构建都要压缩代码,后来把生产环境插件单独拎出来,热更新速度快到飞起。
还有个小技巧是设置resolve参数,webpack解析模块时会去node_modules里找依赖,但如果项目里有很多自定义的组件或工具函数,每次都要写长长的相对路径,webpack找起来也费劲,可以在resolve.alias里设置别名,比如把src/components设为@components,这样不仅写代码方便,webpack解析时也能少走弯路,我现在每个项目都会配:resolve: { alias: { '@components': path.resolve(__dirname, 'src/components') } },既提高开发效率,又加快构建速度。
提升webpack构建速度的缓存策略
如果说基础配置是“节流”,那缓存策略就是“开源”——让webpack记住之前的工作成果,下次直接复用,不用重复劳动,我自己踩过最大的坑就是忽略了缓存,导致每次构建都像“重新开荒”,后来把缓存用明白了,构建时间直接砍半,简直是“真香”现场。
最基础的缓存是开启webpack内置缓存,webpack 5以后自带了缓存功能,只需要在配置里加一句cache: true,就能让webpack把编译结果缓存到硬盘上(默认在node_modules/.cache/webpack),我之前那个15分钟的项目,加了这行配置后,第二次构建直接降到8分钟,第三次甚至能到5分钟,就像老师批改作业,第一次要逐字看,第二次只看修改过的部分,效率当然高。
进阶一点可以用cache-loader或hard-source-webpack-plugin,cache-loader可以缓存loader的处理结果,适合那些处理耗时的loader,比如babel-loader、vue-loader,使用时把cache-loader放在其他loader前面,比如use: ['cache-loader', 'babel-loader'],它会把babel处理后的结果存起来,下次如果文件没变化,直接用缓存,我试过给babel-loader加cache-loader,处理100个js文件的时间从3分钟降到了1分钟,效果立竿见影。
hard-source-webpack-plugin则是更强大的缓存插件,它会为模块创建中间缓存,不仅缓存loader结果,还会缓存模块依赖关系,速度提升更明显,不过要注意,webpack 5以后内置的缓存已经很强了,这个插件可能有点“画蛇添足”,但如果是webpack 4及以下版本,强烈推荐试试,我之前维护一个webpack 4的老项目,用了hard-source-webpack-plugin后,构建时间从12分钟降到了4分钟,团队小伙伴都惊呆了。
还有一个细节是热更新缓存,devServer的hot: true开启热更新后,webpack会只更新修改过的模块,但有时候热更新还是慢,可能是因为缓存没生效,可以在devServer里设置inline: true(默认开启),让热更新通过inline模式注入,减少网络请求;或者用webpack-dev-middleware配合webpack-hot-middleware,手动控制热更新逻辑,我之前有个项目热更新要30秒,后来发现是devServer配置里少了hot: true,加上之后热更新秒级响应,改完代码立马就能看到效果,开发体验直接拉满。
webpack构建速度优化工具对比
除了配置和缓存,还有很多专门的工具能帮webpack“提速”,就像给汽车换引擎,选对了工具,构建速度能“起飞”,我试过市面上主流的几个优化工具,各有优缺点,今天就来给大家做个横向对比,帮你选到最适合自己项目的“加速器”。
第一个是thread-loader,它的作用是把loader的工作分给多个线程处理,适合CPU密集型任务,比如babel-loader处理大量js文件时,单线程处理慢,用thread-loader就能让多个CPU核心同时工作,使用方法很简单,把它放在其他loader前面,比如use: ['thread-loader', 'babel-loader'],不过要注意,thread-loader有启动成本,如果单个loader处理时间很短,用它反而会变慢,适合处理100个以上js文件的项目,我之前在一个有300多个组件的React项目里用了thread-loader,babel处理时间从5分钟降到了2分钟,效果很明显。
第二个是esbuild-loader,它用Go语言写的esbuild工具替代babel处理js/ts文件,速度比babel快10-100倍,简直是“火箭级”加速,我之前把项目里的babel-loader换成esbuild-loader,同样处理100个js文件,babel要3分钟,esbuild-loader只要20秒,快到不敢相信,不过esbuild的兼容性不如babel,有些新语法可能不支持,适合对兼容性要求不高的项目,或者作为开发环境的临时方案。

第三个是swc-loader,和esbuild类似,也是用Rust语言写的,速度比babel快,兼容性比esbuild好一些,支持更多babel插件,我试过在一个需要兼容IE11的项目里用swc-loader,虽然比esbuild慢一点,但比babel快3倍,而且能正确处理IE兼容的语法转换,算是“平衡型选手”。
第四个是terser-webpack-plugin,虽然它是用来压缩js的,但开启缓存和多线程后,压缩速度也能提升不少,在生产环境构建时,压缩代码往往占总时间的30%以上,用terser-webpack-plugin的parallel: true开启多线程,再加上cache: true缓存结果,压缩时间能减少一半,我之前一个项目生产构建要20分钟,其中压缩占了8分钟,优化后压缩时间降到3分钟,整体构建时间缩短到12分钟。
对比下来,如果是开发环境,追求极致速度选esbuild-loader;需要兼容性选swc-loader;CPU核心多就加thread-loader。生产环境,用terser-webpack-plugin多线程+缓存压缩;如果项目大,thread-loader配合babel-loader也不错,选工具就像选运动鞋,得根据自己的“脚型”(项目需求)来,不是越贵越好,合适才重要。
webpack构建速度优化实战案例
光说理论太空泛,今天就分享一个我自己优化过的真实项目案例,从15分钟构建到3分钟,全程踩坑到解决,新手照着做也能学会,这个项目是一个基于Vue 2的管理系统,有300多个组件,100多个依赖包,之前每次npm run dev都要等15分钟,热更新5分钟,开发团队怨声载道,我接手后花了3天时间优化,现在构建只要3分钟,热更新3秒,团队效率直接翻倍。
第一步是诊断问题,我先运行webpack --profile --json > stats.json生成构建报告,再用webpack-bundle-analyzer分析,发现问题有三个:一是node_modules里的依赖没排除,webpack在解析所有依赖;二是没开缓存,每次都重新编译;三是用了完整版的element-ui,体积太大,报告就像医生的CT片,一下子找到了“病灶”。
第二步是基础配置优化,针对依赖解析问题,我给babel-loader加了include: path.resolve('src')和exclude: /node_modules/,这样webpack就只处理自己写的代码,不碰node_modules;然后在resolve.extensions里只保留常用的.js、.vue、.json,减少webpack的文件查找时间;最后把dev配置里的productionSourceMap设为false,sourcemap生成超占时间,开发时直接关掉,这一步做完,构建时间从15分钟降到了10分钟。
第三步是缓存策略生效,在webpack配置里加了cache: { type: 'filesystem' },开启文件系统缓存;又给babel-loader前面加了cache-loader,缓存编译结果,这时候再构建,第二次直接降到6分钟,热更新也从5分钟变成了2分钟,团队小伙伴开始有笑容了。
第四步是工具加持,我把babel-loader换成了esbuild-loader,因为项目是内部系统,不需要兼容旧浏览器,esbuild速度更快,配置很简单:use: { loader: 'esbuild-loader', options: { loader: 'jsx', target: 'es2015' } },这一步是“暴击”,构建时间直接从6分钟砍到3分钟,热更新3秒完成,改完代码保存,浏览器瞬间刷新,简直不要太爽。
最后一步是按需加载依赖,之前项目直接import整个element-ui,我改成了按需引入,用babel-plugin-component只加载用到的组件,比如只引入Button和Table,这样打包体积小了,解析时间也少了,虽然这一步对构建速度影响不如前几步大,但能让最终包体积减少40%,算是“锦上添花”。
整个优化过程就像给老车做保养,先检查问题(诊断),换机油滤芯(基础配置),加高性能燃油(缓存),换涡轮增压器(esbuild-loader),最后减轻车载重量(按需加载),一步步下来,老车也能跑出新车的速度,如果你也遇到构建慢的问题,照着这个步骤试一遍,大概率能解决。
webpack构建速度优化注意事项
优化webpack构建速度虽然能提升效率,但如果操作不当,反而会“好心办坏事”,比如引入bug、增加维护成本,甚至越优化越慢,我之前就见过有人为了追求速度,乱改配置,结果项目跑不起来,最后还得回滚,今天总结几个优化时必须注意的“避坑指南”,帮你少走弯路。
第一个注意事项是不要过度优化,有些同学看到一篇优化文章就把所有方法都用上,结果插件装了十几个,配置文件搞得像天书,不仅没提速,还因为插件冲突导致构建失败,我之前帮一个项目优化,发现他们同时用了hard-source-webpack-plugin、cache-loader、esbuild-loader,结果三个工具缓存逻辑冲突,构建时不是报缓存错误就是文件丢失,后来删掉hard-source-webpack-plugin,只保留cache-loader和esbuild-loader,问题解决,速度也没下降,优化是为了解决问题,不是炫技,够用就行。
第二个是区分开发和生产环境,开发环境和生产环境的目标完全不同:开发环境要快,要热更新,不需要压缩代码、提取css;生产环境要小,要兼容,需要各种优化插件,如果把生产环境的插件放到开发环境,比如TerserPlugin、OptimizeCssAssetsPlugin,杀鸡用牛刀”,纯属浪费性能,我之前见过一个项目,dev配置里居然有CompressionPlugin(压缩成gzip),构建时既要压缩代码又要压缩成gzip,速度能快才怪,去掉之后开发构建时间直接少了一半。
第三个是注意工具兼容性,现在很多优化工具都是新出的,和webpack版本、其他插件可能不兼容,比如esbuild-loader目前对webpack 5支持较好,但对webpack 4可能有bug;thread-loader不能和url-loader、file-loader一起用,会导致文件路径错误,我之前在一个webpack 4项目里用esbuild-loader,结果报了一堆语法错误,查了文档才发现esbuild-loader对webpack 4的支持有限,后来换成swc-loader才解决,用新工具前一定要看官方文档的兼容性说明,别凭感觉瞎用。
第四个是备份配置文件,优化过程中难免会改坏配置,这时候有个备份就能快速回滚,我每次优化前都会把webpack.config.js复制一份,命名为webpack.config.bak.js,万一改崩了,直接覆盖回来,省时省力,有一次我尝试手动修改SplitChunksPlugin的配置,结果把chunk分割得乱七八糟,页面加载时js报错,还好有备份,1分钟就恢复了,没影响开发进度。
最后一个是定期清理缓存,缓存虽然能提速,但有时候缓存文件会损坏,导致构建错误或结果异常,比如改了babel配置,缓存里还是旧的编译规则,就会出现语法转换错误,这时候需要手动删除node_modules/.cache/webpack目录,让webpack重新生成缓存,我一般每周清理一次缓存,避免出现“灵异bug”,这个习惯帮我解决过