起
去年12月份,我一个才毕业半年的前端新人接触了Webpack。跟着网上教程学习时,遇到了一个难题:文件名加上hash编码后,多次打包会有之前残留的文件。
当时看的那篇文章,并没有提到clean-webpack-plugin
,所以我就傻傻的手动删除。还很奇怪为什么这么严重的问题没人说过……其实就是当时google的关键字有问题,没搜到正确的结果。
不过也是那次误打误撞,让我开始维护一个自己的Webpack插件——webpack-remove-hashed-files。具体功能可以看我之前的文章。
承
本来这个插件吧,功能也不是很复杂,所以我开发好第一版后就没管了。只有后面收到一封邮件,按要求我新增了白名单的功能。接着就搁置了三个月吧。
不过呢,昨天在新项目里需要用到几个本地的图片,于是就发现了一个bug。
就是说,我在项目中本来要用file-loader
去处理js和css中引入的本地图片,将他们打包到dist
文件夹下。然后呢,我打算把它们都放在dist/assets/images
子文件夹下。于是就配置file-loader
,option为{name: 'assets/images/[name].[ext]'}
。
然而就是这个配置导致我的删除插件出了问题。
本来我插件的设计是遍历dist
目录,将所有文件的文件名与Webpack的compliation.assets
的键名比较,如果文件名能够对应于某一个键名,说明是本次打包的内容,将其保留。其他的都删除。这是不考虑白名单的思路。
但是,不知道为什么,file-loader
处理后的文件,在assets
中是以类似于assets/images/a.jpg
的键名保存的,这就导致遍历dist
文件夹时,我是用a.jpg
与assets/images/a.jpg
做比较。虽然这是同一个文件,但实际上被误认为两个文件,从而导致删除bug。
那么我该怎么解决呢?
转
首先要明确问题出现所涉及到的两个角色:file-loader
与webpack-remove-hashed-files
。其次,能够修改的是file-loader
的options与删除插件的源码。而我的目的就是要把所有图片都存放在dist/assets/images
文件夹下,所以file-loader
的options是不能修改的,应该重构插件的源码来适应file-loader
的配置。
说实话,昨天想了一天才解决好这个问题。因为一直没明白,file-loader
到底是怎么做到又改文件名又能创建个新文件夹的……
继续说怎么修改删除插件的源码吧。我苦思冥想N个小时后,决定推翻以前的判断逻辑。因为我想到了一个更简单的判断因素,而且这个因素才是删除插件的核心,只是一直被我忽略了。那就是【文件路径】。
在compliation.assets
中,每一键对应一个文件对象,其中有一个字段existsAt
表明打包后的文件所在的绝对路径。那么我们先将这些路径保存到一个数组dirArrays
中,然后在遍历dist
下所有文件时,在dirArrays
中搜索每个不在白名单中的文件的绝对路径,如果存在则保留;不存在则说明不是本次打包的内容,应该删除。
听上去是不是既不影响原有功能,而且还能解决file-loader
的bug问题?另外还比之前的逻辑要简单一些。
至于为什么说文件路径是删除插件的核心?因为插件实质上做了两件事:比较assets与dist
中所有文件、删除非本次打包产生的文件。原本只有在删除时才要调用fs.unlink
方法,传入文件路径参数。而现在在比较的时候就使用了文件路径。说明它贯穿了本插件的所有流程,说是核心也不为过了。
合
以上就是本次优化插件的完整记录了,总结一下就是自己造的轮子要多测试多维护,这样才能有理由写推广的文章,让更多人去使用hhh。
另外,在遇到bug后我分析了一下竞品clean-webpack-plugin
,发现它的思路跟去年我看得时候相比,还是默认在生命周期emit
时遍历目标文件夹dist
,如果文件不在白名单里就直接删除。这点也是它能够不受file-loader
影响的原因。
但也是我一开始文章中说过的,本次打包与上一次打包也许只有一个文件被修改,实际上只要删除旧的这一个文件就行,但clean-webpack-plugin
是将所有文件都删除了。我觉得这一点在大项目中还是会导致打包时间问题的,所以就自己造了个轮子。
还发现了一点,就是判断了是否有compiler.hooks
,这是Webpack 4的新特性。后面我再修补下这个问题。
最后,感谢阅读。