处理css中的图片资源时,我们常用的两种loader是file-loader或者url-loader,两者的主要差异在于。url-loader可以设置图片大小限制,当图片超过限制时,其表现行为等同于file-loader,而当图片不超过限制时,则会将图片以base64的形式打包进css文件,以减少请求次数。
本文的主要想说的是我们在使用file-loader或url-loader时经常出现的图片地址错误导致图片引用不到的情况,及相应解决办法。
场景复现
工程目录如下
工程目录
index页面目录
webpack配置文件中,关于图片的处理:
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'img/[name].[hash:7].[ext]'
}
},
index.js文件
import './index.scss'
index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="pic"></div>
</body>
</html>
index.scss文件:
.pic{
width:100px;
height:100px;
background: url('./1.jpg');
background-size: 100px 100px;
}
当我们执行 npm run build
后 查看dist中的情况如下:
我们通过服务 打开index.html会发现页面中并没有 图片,并且报错:
image.png
这是我们当然是要去看看 打包后的 css文件中的 图片路径了:
.pic{width:100px;height:100px;background:url(img/1.d1efbb3.jpg);background-size:100px 100px}
/*# sourceMappingURL=index.min.css.map?v=7b7c89f8*/
background:url(img/1.d1efbb3.jpg);
,index.min.css文件竟然去自己的同级目录找img文件夹,当然找不到了
还记得我们的 dist目录情况么?
很显然
img
文件夹位于index.min.css
上层,如果 是background:url(../img/1.d1efbb3.jpg);
就对了。
然后我们尝试去修改webpack的配置文件,以达到我们的预期:
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: '../img/[name].[hash:7].[ext]'
}
},
然后再npm run build
index.min.css 内的 background的图片地址的确变成了 我们想要的
.pic{width:100px;height:100px;background:url(../img/1.d1efbb3.jpg);background-size:100px 100px}
/*# sourceMappingURL=index.min.css.map?v=eef74865*/
但是悲剧的是!!!!! img 竟然被打包到了 dist文件夹的外面,不在位于dist内了,所以
现在的dist 之前的dist是不是很抓狂……看来我们修改配置文件的方法是不对的。仔细想想,能明白其中的原因么?
打包的时候webpack会把 scss文件中的 background url 替换成我们webpack配置文件中的 options的name属性中设置的内容,同时把 scss文件中的 background url 中的图片文件 给复制到 webpack配置文件中的 options的name属性所指向路径下,关键就在这里了。
webpack配置文件中的 options的name属性所指向路径 是相对路径,那么这个路径到底是相对于谁呢?你仔细观察 会发现 它是相对于 dist
文件夹的,也就是webpack的出口路径。举个例子:
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'img/[name].[hash:7].[ext]'
}
},
上面的配置,就会把图片 给复制到 dist/img/1.jpg
,然后 将index.min.css 你的 background属性 改为background:url('img/1.png')
,该路径也是相对路径,但是它不相对于 dist
,而是相对于dist/css
,因为我的css文件 并没有被输出到 dist
的直接路径下,而是输出到了dist/css
下,所以css 文件就会去dist/css/img/1.png
去拿图片,但是图片却位于dist/img/1.png
,这就最终导致了 css文件找不到图片。
配置文件复制图片是相对于 webpack 出口路径的 , css文件引用图片是相对于css文件所在路径的,如果这两个路径相同 也就是 webpack出口路径 = css文件所在路径
那么 很幸运 ,你的图片是可以找到的。但是一般情况下是不同的,我们习惯于 将出口路径 定为dist/
然后将css文件输出到 'dist/css/' ,最终导致了引用不到图片的结果。
至此原因分析完毕。
解决方法:
在配置项内加入 publicPath 属性,设置为部署时的绝对路径
比如所 以后你的页面 会通过如下url方式让用户访问,所有前端文件都放置于
http://localhost:63342/url-loader-test/dist/
那么pubilcPath的 值就应该是 '/url-loader-test/dist/'
,也就是你的部署接口地址。
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'img/[name].[hash:7].[ext]',
publicPath:"/url-loader-test/dist/" //该地址不是唯一的,根据你的代码实际路由地址进行修改
}
},
这样做的原因是,webpack打包时,还会将图片复制到 dist/img/1.png
,但是他会把css文件中的background url 改写为 publicPath + name
,本例中最后生成的index.min.css 如下:
.pic{width:100px;height:100px;background:url(/url-loader-test/dist/img/1.d1efbb3.jpg);background-size:100px 100px}
/*# sourceMappingURL=index.min.css.map?v=eef74865*/
这时css文件中的url 地址就变成了一个绝对 路由。
至此,问题解决。
网友评论
说实话,webpack比较傻。各个功能插件彼此独立但又耦合(比如fileloader的publicpath和mincssextract的publicpath,两个设置有覆盖又有耦合关系。烦的一逼)。
我html和css中都引用了图片,html在dist根目录下,css在idist/mg目录下,使用相对路径生成的图片路径是一样的,但是html和css目录结构不一样,导致总有一方引用不到图片.现在把output的publicPath参数改成绝对路径解决了问题.
项目地址:https://github.com/yanxiaojun617/webpack4-bootstrap4-demo/
能指导一下吗