url-loader / file-loader breaking relative paths in css output using webpack
我正在将Webpack与一些插件和加载程序结合使用,以获取src /文件夹并构建dist /文件夹。 url-loader(当图像大于特定限制时回退到文件加载器)正在将它在我的html和scss文件中找到的图像输出到预期的正确目录。但是,它会更改这些文件中的相对路径,从而输出具有错误路径的css文件。
文件结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | src/ index.html assets/ js/ index.js scss/ style.scss img/ pic.jpg background.jpg dist/ index.html assets/ js/ index.js css/ style.css img/ pic.jpg background.jpg |
您可以看到我的dist /文件夹镜像了我的src /文件夹,除了scss被编译为css。
src / index.js导入index.html和style.scss,以便那些文件可以由webpack解析,并且其中的任何图像都可以由url-loader处理:
index.js
1 2 | import '../../index.html'; import '../scss/style.scss'; |
style.scss使用相对路径在主体上设置背景图像:
style.scss
1 2 3 | body { background: url('../img/background.jpg'); } |
index.html仅使用相对路径显示图像:
index.html
1 | <img src="./assets/img/pic.jpg" alt="pic"> |
我使用HtmlWebpackPlugin在我的html文件中进行复制,因为它允许我指定要自动包含为脚本标签的块。对于css,我要么使用开发中的style-loader将其注入html文件,要么使用MiniCssExtractPlugin将其提取到生产环境中的自己的文件中。
但是,当webpack解析index.html和style.scss时,相对路径分别替换为\\'assets / img / pic.jpg \\'和\\'assets / img / backgorund.jpg \\'。这不会破坏index.html中的路径,因为它实际上是相同的相对路径,但这显然是style.css的问题。如何停止url-loader更改相对路径,或仅生成正确的路径?还要注意,当在开发中使用样式加载器将css注入html时,该路径有效,因为该路径是相对于html文件的。理想情况下,webpack应该能够生成正确的相对路径,具体取决于我是在生产中提取css还是在开发中注入css。
我尝试使用resolve-url-loader,指定publicPath和outputPath,当然也可以在线搜索答案,但是运气不佳。
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | const path = require('path'); const webpack = require('webpack'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const devMode = process.env.NODE_ENV !== 'production'; module.exports = { mode: devMode ? 'development' : 'production', entry: { index: './src/assets/js/index.js', }, output: { filename: 'assets/js/[name].js', path: path.resolve(__dirname, 'dist') }, devServer: { contentBase: path.join(__dirname, 'src'), watchContentBase: true, hot: devMode, }, devtool: devMode ? 'source-map' : '(none)', plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ filename: 'index.html', template: 'src/index.html', }), new MiniCssExtractPlugin({ filename: 'assets/css/style.css', }) ], module: { rules: [ { test: /\\.html$/, use: [ { loader: 'html-loader' } ] }, { test: /\\.(jp(e?)g|png|svg|gif)$/, use: [ { loader: 'url-loader', options: { limit: 8192, name: 'assets/img/[name].[ext]' } } ] }, { test: /\\.scss$/, use: [ { loader: devMode ? 'style-loader' : MiniCssExtractPlugin.loader }, { loader: 'css-loader', options: { sourceMap: devMode, importLoaders: 2 } }, { loader: 'sass-loader', options: { sourceMap: devMode } } ] } ] } }; if (devMode) { module.exports.plugins.push(new webpack.HotModuleReplacementPlugin()); } |
已解决
解决了感谢github上的这篇文章:https://github.com/webpack-contrib/mini-css-extract-plugin/issues/44#issuecomment-379059788。
只需将publicPath选项添加到MiniCssExtractPlugin中,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | ... { test: /\\.scss$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: '../../' // path to director where assets folder is located } }, { loader: 'css-loader', options: { sourceMap: devMode, importLoaders: 2 } }, { loader: 'sass-loader', options: { sourceMap: devMode } } ] }, ... |
要在开发模式下使用样式加载器,而不是像我最初的webpack.config.js中那样使用MiniCssExtractPlugin,则必须有条件地添加该选项,因为样式加载器不接受publicPath选项。我是在webpack.config.js的底部这样做的,例如:
1 2 3 | if (!devMode) { module.exports.module.rules[0].use[0].options = { publicPath: '../../' }; } |
然后确保rules数组中的第一个对象用于scss。有条件地添加此内容的一种凌乱方法,但现在可以使用。
尝试添加publicPath选项
1 2 3 4 5 6 7 8 | { loader: 'url-loader', options: { limit: 8192, name:"[name].[ext]" publicPath: '/assets/img/ //<-- assuming assets is in web root } } |
然后将style.scss更改为
1 2 3 | body { background: url('background.jpg'); } |
花了很长时间!缺少以下
1 2 3 | output: { publicPath: '/' }, |
这是我的解决方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | const devMode = process.env.NODE_ENV !== 'production'; ...rules: [ { test: /\\.scss$/, use: [ devMode ? 'style-loader' : { loader: MiniCssExtractPlugin.loader, options: { publicPath: '../', } }, { // translates CSS into CommonJS loader: 'css-loader', options: { sourceMap: true, }, }, { // Autoprefixer usw. loader: 'postcss-loader', options: { ident: 'postcss', }, }, { // compiles Sass to CSS, using Node Sass by default loader: 'sass-loader', options: { sourceMap: true, }, } ], }, ] |