一、省流
webpack5
下,css-loader
与 file/url-loader
对于 css 文件(无论是 import 一份 css 文件还是 vue 里面的 style 块)里的 background-image: url()
资源文件处理有冲突。表现在:
打包出来的 url 使用的是 css-loader
自己产出的图片。但是这张图片生成有问题,导致图片无法显示。
解决方法看最后面。
二、溯源
根据《react 基础工程(react-redux & react-router)》里的配置,里面配置了 file/url-loader
去处理资源文件。
{
test: /\.css$/,
use: [
isDev ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
],
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
esModule: false,
limit: 8192,
name: 'media/[name].[hash].[ext]',
},
},
],
}
对于普通使用并没有问题。但是后面使用了 background-image: url()
发现图片显示不出来。去产物文件夹(我这里是 dist 文件夹)一看,发现在根路径多了几张图片,但是打不开,显示图片格式损坏。而配置 name: 'media/[name].[hash].[ext]'
里指定的 media 文件夹也有正常的图片。但是一看处理后的 css 使用的却是那一堆在根路径的损坏的图片。
也就是:background-image: url() 没有使用 file/url-loader 打包出来的图片,而是使用了别的 loader 产出的图片,而且这堆图片还生成失败了。
把 file/url-loader
去掉后,生成的图片正常了。也就是和这两个冲突了。接着看看问题在哪。插一句:我是在一个旧项目做了升级的,之前用的低版本也就是 webpack4 以及其他一系列低版本的配套是可以的,升级了后才出现问题。
一开始我怀疑是 mini-css-extract-plugin
,怀疑是抽取 css 文件的时候,顺便生成了图片。后面我把这个去掉,只留下 css-loader
发现也会产出图片。这我着实没想到,以为只是单纯处理 css,遇到资源文件还是交给 file/url-loader
。但是 webpack4 是好的,或者 webpack5 没有 file/url-loader
也是好的。
三、查找
那就缩小了排查范围,就着这几个关键字进行了搜索,终于发现问题:webpack5 对于资源文件处理进行了更改,具体可看官网《资源模块》介绍。也就是 webpack5 不需要再对资源文件指定相应的 loader 去处理了。而是加上 type
声明,比如 type: asset/resource
。而如果我们还按照老的配置,可能会对资源重复处理,从而导致生成了一堆没法用的损坏图。
确实是 css-loader
和 file/url-loader
有冲突,决定性证据也懒得去看源码了,这 webpack 4 升 5 真的改变好大。还记得刚出那会一大堆插件不兼容,不升级都没法用。
四、解决
既然知道问题了,那么就有以下解决方案:
- 继续使用 file/url-loader,但是要加上
type: 'javascript/auto'
声明。看官网例子:
module.exports = {
module: {
rules: [{
test: /\.(png|jpg|gif)$/i,
use: [{
loader: 'url-loader',
options: { limit: 8192 },
}],
type: 'javascript/auto', // 加上这一句
},
]},
};
结果:css-loader 不会再在根路径产出图片,打包后的 css url 使用的是 file/url-loader 生成的图片。
- 继续使用 file/url-loader,指定不要参与
url
的处理:
module.exports = {
module: {
rules: [{
test: /\.(png|jpg|gif)$/i,
use: [{
loader: 'url-loader',
options: { limit: 8192 },
}],
dependency: { not: ['url'] }, // 加上这一句
},
]},
};
结果:css-loader 和 file/url-loader 依然在各自地点生成图片,css url 使用的依然是 css-loader 在根路径产出的图片。各论各的,毫无疑问容易造成冗余,以及文件位置不统一。
- 完全改为 webpack5 的写法,不用再指定 loader 去处理:
module.exports = {
module: {
rules: [{
test: /\.(png|jpg|gif|svg)$/i,
type: 'asset/resource',
generator: {
filename: 'media/[name].[hash].[ext]',
},
},
]},
};
结果:不会再在根路径产出图片,打包后的 css url 使用的是统一位置生成的资源文件。
- 或许还有其他,够用了…
五、推荐
方法二明显不太合理,淘汰。简单修改可以用方法一。不嫌麻烦还是推荐改成方案三。
(再吐槽一句,这算不算 webpack 的背刺?)