一次antd.css引入引发的webpack分包问题

768 阅读7分钟

背景介绍

同事:xx,之前一个老项目(也不算太老,大概2年半前构建的架子吧)想在里面用antd的组件,结果引入antd的样式文件就报错... 我:怎么会呢,不就是引入个antd么,光引入都报错了?引入的姿势不对?是不是之前的一些webpack的插件版本低了?和antd里面的不兼容? 同事:对啊,光引入就报错了,很神奇。也试了下低版本的antd,还是报错啊。 我:额,我咋那么不信呢,我来试试 于是乎,我拉了下代码,然后也引入了antd的组件,没问题。但是一旦引入了样式文件之后,就gg了,错误如下:

WX20220625-113435@2x.png

一次查找bug之旅就此开启,demo git地址这里获取(还有一个未解决的,需要大伙一起看看)...

开启旅程

  • 步骤一:环境重现 当然,我是先根据一定的分析定位,知道了大概的问题,然后再针对性的去重现这个环境配置。老项目东西比较多,我打算把这个webpack的配置一模一样的在一个临时的demo中拷贝,方便更好地定位问题。这个过程也不是简单的copy即可(因为简单copy过去没有跑起来,哈哈哈),过程中编译的时候又报出了很多错误,都是一些关于node版本、npm版等(node版本v14.16.1,npm版本6.14.12)。webpack的配置optimization里面有利用splitchunks分包的,我就随便安装了几个包,做到差不多一模一样的情景。果然,引入了antd的样式文件后引来了一样的错误,开心😄(第一次写出bug还这么开心)。一些重要的配置如下所示(分别是,入口一index.js,两入口配置,splitchunks配置):

WX20220625-121653.png WechatIMG532.png 1111.png

  • 步骤二:错误分析 通过控制台,发现是node_modulexxx.css报错了,然后来到网络查看下对应的文件,结果如图: css.png 发现没,并没有node_modulexxx.css,而且,根据分包的规则这里的js是不是还少了一个包呢?根据上面的配置,你们觉得这里最终会生产几个chunk(我这里习惯叫做包),可以思考下。总结的分包规则如下:
  1. 一个入口会分出一个chunk;
  2. 按需加载每个按需的部分会分出一个chunk;
  3. webpack runtime时的一些helper函数也会分出一个chunk,前提是在有配置的情况下,默认不会分出;
  4. 根据optimization里面splitChunks的配置,会根据配置规则生产相应的chunk,比如被多个入口引用了多次,或者某些库文件需要单独的分包等等。 根据上述原则,那么我们最终的打包结果是不是还少了一个node_module.js包。那么为什么呢,明明已经配置了那,这个时候再去看看splitChunks的其他配置,我们发现了maxInitialRequests这个属性被设置成了3,那这个属性是用来干嘛的呢,看看webpack官网的解释:

splitChunks.maxInitialRequests number = 30 Maximum number of parallel requests at an entry point.

限制了一个入口的最大请求数,它的设置对分包的数量也进行了限制,避免因为分包太多而得不偿失。这里设置成了3,所以当前的分包数超过了限制值,以maxInitialRequests的值为上限。我们尝试着把这个值改大一些比如10,是不是就可以了呢😈,再来看下结果: 2222.png 没问题,该拆分的包都在,但是这个时候页面白屏了,哈哈哈,祸不单行。为啥呢,明明该拆分的包都拆出来了啊,再看看另外一个入口home的页面呢,也是什么都没有。如果是你会想到什么问题呢?真的所有的包已经被“拆分”出来了么,查看network下你看到了所有的包了么?还差啥?这时候,让我想起了splitChunks另外一个属性值minChunks,看看webpack的解释: splitChunks.minChunks(number=1),The minimum times must a module be shared among chunks before splitting.

如果一个包在多个入口间被引入的次数超过minChunks的限制,那么该包就会被拆分。这里没有设置minChunks,所以用了默认值,因为index和home的项目里面都用了react,所以改公共包被拆分了。但是被拆分了,怎么没有出现在index或者home的页面中呢。关于js插入最终html的问题不得不让我们想到了对应的插件html-webpack-plugin,查看下对于改插件的配置:

3333.png 可以看到,对于不同的项目,我们通过html-webpack-plugin指定其包含的chunks,但是没有引入俩项目的公共部分,修改下这里引入二者的公共包home~index,再试试。

4444.png

哈哈哈哈,大功告成,当然这时候我也如约看到了页面的呈现。我们来回顾下,首先是maxInitialRequests的限制,导致有些包并未“如约而至”,可以看出它的优先级是大于cacheGroups配置的优先级的。然后由于minChunks导致公共包被拆分,但是由于html-webpack-plugin chunks的原因没有引入公共包导致页面没有渲染。到此,我们再来看看几个问题。

持续思考

  1. maxInitialRequests的限制只针对js么,因为在network里面看到了对应的css也被拆分出来,如果加上css的包,是不是也会对css的拆分也会有限制呢 根据查阅相关资料以及修改demo做尝试,得出以下结论:
  • 动态加载而拆分出来的包不算在内
  • 只计算js的文件数量,css不算在内
  • 如果同时有两个模块满足cacheGroup的规则要进行拆分,但是maxInitialRequests的值只能允许再拆分一个模块,那尺寸更大的模块会被拆分出来。
  1. 为什么要指定html-webpack-plugin chunks的配置呢,不配置有什么影响 不配置也可以,但是如果是多个入口,那么所有的被分包的js都会被加入到项目中来,拿我的项目来说,home里面的home.js也会被加入到inde的项目中来。但是如果强制配置了chunks,可能就会发生想现在这样有些被拆分的包,特殊原因没有被我们引入到项目中来,这个怎么去避免呢。仔细查看html-webpack-plugin的git,它为我们提供了几个方案解决这种模版的问题,所以我不是很建议通过设置chunks去解决各个项目需要哪些特定的包的做法。

  2. 为什么引入antd.css导致了分包的问题呢,引入antd的js确不会呢 其实不仅是antd的css引入别的css,比如引入一个swiper的css进来,也会导致同样的问题。讲道理来说,分包就算不成功,那也只是说导致有些包没有拆分出来,那包应该也还是在的呐,也不至于会造成上面的问题。而且,如果我们通过设置splitchunks对antd的包单独进行拆分,而不是让它处于node_modules中,也不会出现报错的情况。关于css的分包不得不让我想到了另外一个插件MiniCssExtractPlugin,我觉得是因为它对css分包不成功继而引发了别的问题。顺着这个猜想,我区分了开发环境和生产环境(本来也该要区分的),在开发环境不进行css的拆包,而是用style-loader去直接加载,生产环境还是继续拆分。这样的情况下,开发环境也还是正常运行,但是生产环境还是包了上面的错误。查阅了很多资料,一直没有得到解决,所以希望大家一起研究下整个过程,帮助找出最终的“元凶”。

一点感悟

可以看出,这个问题其实也不算什么比较大的问题,但是细细的研究其背后涉及到的知识点也不少,比如关于webpack最终会将包进行怎样的拆分,关于splitchunks的一些配置你了解多少等等。有些知识官方给出描述有限,需要我们自己去寻找答案。处处留心皆学问,不要再说你做的项目不够高大上,把每一处知识点细细拆解,专心的去研究,总有一天你会发现不知不觉中你已经成长了不少。

相关链接

案例git地址

webpack-splitchunks

html-webpack template-opton

关于maxInitialRequests