之前在使用webpack3构建基于less预处理的项目时,在对指定的元素使用background-image: url(xxx)
来设置背景图片时,本地开发是ok的,但是在项目编译产出后背景图片就找不到;目前用webpack4开发项目时,同样遇到类似的问题;所以就借此机会探讨一下产生问题的原因。
问题产生原因
webpack3项目场景复现
项目webpack有关css的配置伪码如下:
output: { // 项目编译输出路径
path: path.resolve(__dirname, \'dist\')
}
// 图片的loader的配置如下:
{
test: /\.(gif|png|jpe?g)(\?\S*)?$/,
loader: \'url-loader\',
options: {
limit: 3000,
name: path.join(\'static\', env === \'development\' ? \'img/[name].[ext]\' : \'img/[name]_[hash:7].[ext]\')
}
}
// 样式文件打包产出的文件配置如下:
if (env === \'produdtion\') {
webpackCfg.plugins.push(
new ExtractTextPlugin({
filename: \'static/index_[contenthash:7].css\',
disable: false,
allChunks: true
})
)
}
上面配置的css在生产环境用extract-text-webpack-plugin
来产出css样式文件,开发环境通过style-loader
将css内容内联到html文档中;而图片是大于指定的limit大小就打包输出文件。
此时在我们的index.less
文件中设置body的背景图片如下:
body {
background-image: url(\'./img/bg.png\');
}
此时本地开发可以看到背景图片,而编译产出css的文件,背景图片地址不能正确加载,通过测试发现是图片路径出现问题,如下图:
从产出的css文件内容来看,body元素的background-image的图片url相对地址是webpack配置产出图片路径,但是页面实际展示时却发现图片路径为:/xx/../dist/static/static/img/bg_ebdbe98.png。
为什么会背景图片路径会多了一个static
前缀?查询资料发现:
css文件中设置的background-image的url相对地址是相对于当前css文件目录来得到的
因为,项目中设置css的产出路径为dist/static/index_[contenthash:7].css
,而url中经webpack处理后的url相对地址为static/img/bg_[hash:7].png
; 这样根据上述规则,图片实际加载地址为即dist/static/static/img/bg_[hash:7].png
,导致会在图片路径前缀多加了个static目录。
而为啥本地开发环境没有出现问题,这是因为本地开发环境产出的css样式内容通过style-loader
内联到html文档中,这是背景图片的路径是相对于html文档目录,所以是正确的。
webpack4项目场景复现
webpack4项目与webpack3项目不同的地方是,webpack4项目中使用mini-css-extract-plugin
插件来处理css样式产出,其改善extract-text-webpack-plugin
中的一些问题,其中比较重要的一点是可以使用css的热加载功能。项目中有关css抽出的配置如下:
plugins: [
new MiniCssExtractPlugin({
filename: env === \'production\' ? \'static/index.[contenthash:7].css\' : \'static/index.css\'
})
],
module: {
rules: [{
test: /\.less$/,
use: [
{loader: MiniCssExtractPlugin.loader},
{loader: \'css-loader\', options: {...} },
{loader: \'less-loader\', options: {...} }
}]
}
webpack4项目在开发环境和生产环境都使用mini-css-extract-plugin
插件,所以项目都会产出css文件,二者环境都会出现问题;
解决方法
在知道问题产生原因后,也就知道该如何解决问题了。最佳的解决方法如下:
- webpack3在
extract-text-webpack-plugin
的extract方法中单独配置css文件的publicPath
若没有在extract-text-webpack-plugin
配置css的publicPath,则会使用webpack.output.publicPath中值;一旦配置值则css中路径就会相对于新配置的publicPath值。但是这个值配置也是需要注意的。例如,上面文件产出目录:
dist
│ index.html
└───static
│ │ index.js
│ │ index.css
│ └─img
│ │ bg.png
图片的地址为static/img/bg.png,而在index.css中引入了该图片地址,所以图片的相对地址是相对于该css文件的目录,及最终加载的图片地址为static/static/img/bg.png,从而导致错误。此时正确的配置extract-text-webpack-plugin如下:
ExtractTextPlugin.extract({
fallback: \'style-loader\',
use: loaders,
publicPath: \'../\'
});
这样,publicPath: \'../\'配置则是从当前index.css文件的父目录来查找图片。
最终url的路径变成 "../static/img/bg.png"。
- webpack4在
mini-css-extract-plugin
的loader中配置publicPath
webpack4也是配置publicPath,只不过配置方式稍有不同,如下:
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: \'../\',
hmr: process.env.NODE_ENV === \'development\',
},
},
\'css-loader\',
],
},
],
},