使用Webpack时,url-loader / file-loader会破坏CSS输出中的相对路径。

7
我正在使用webpack和一些插件和加载器来处理我的src/文件夹并构建一个dist/文件夹。url-loader(当图像大于特定限制时退回到file-loader),会将在html和scss文件中找到的图像输出到正确目录下。然而,它会更改这些文件中的相对路径,并以此生成一个具有错误路径的CSS文件。
文件结构:
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

import '../../index.html';
import '../scss/style.scss';
使用相对路径在body上设置背景图片:

style.scss

body {
    background: url('../img/background.jpg');
}

index.html只显示一张图片,同时使用相对路径:

index.html

<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更改相对路径,或者生成正确的相对路径?还请注意,在开发中使用style-loader将CSS注入到HTML中时,路径可以正常工作,因为它是相对于HTML文件的。理想情况下,Webpack应该能够根据我在生产中提取CSS或在开发中注入CSS来生成正确的相对路径。
我尝试过使用resolve-url-loader、指定publicPath和outputPath,当然也在网上寻找答案,但都没有成功。 webpack.config.js
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());
}
4个回答

15

已解决

感谢此github帖子:https://github.com/webpack-contrib/mini-css-extract-plugin/issues/44#issuecomment-379059788

只需像下面这样将publicPath选项添加到MiniCssExtractPlugin中:

...
{
    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
            }
        }
    ]
},
...
在开发模式下,如果要使用 style-loader 而不是像我的原始 webpack.config.js 一样使用 MiniCssExtractPlugin,你需要在 webpack.config.js 的底部按如下所示添加条件选项,因为 style-loader 不接受 publicPath 选项。:

要在开发模式下使用 style-loader 而不是像我的原始 webpack.config.js 中使用 MiniCssExtractPlugin,您需要在 webpack.config.js 的底部添加条件选项,如下所示:

if (!devMode) {
    module.exports.module.rules[0].use[0].options = { publicPath: '../../' };
}

然后确保规则数组中的第一个对象是针对scss的。这种有点混乱的方式可以有条件地添加,但现在它足够了。


6

我已经花费很长时间了!以下publicPath设置是缺失的!

 output: {
     publicPath: '/'
 },

3
尝试添加publicPath选项。
 {
    loader: 'url-loader',
    options: {
        limit: 8192,
        name: "[name].[ext]"
        publicPath: '/assets/img/   //<-- assuming assets is in web root
    }
 }

将style.scss更改为

body {
    background: url('background.jpg');
}

然后,由于webpack在style.scss文件中查找图像时会在同一文件夹中查找,所以它无法找到background.jpg。 (模块未找到:错误:无法解析'/Users/jlwalker/demo/src/assets/scss'中的'./background.jpg')。此外,它使dist/输出文件中的路径成为绝对路径,而我最好使用相对路径。 - Jordan Walker
resolve-url-loader出了什么问题?你是不是把它放在了sass-loader之前? - varoons
据我所知,resolve-url-loader允许您在sass局部文件中使用相对于这些局部文件的路径,而不是相对于导入这些局部文件的sass文件的路径。我遇到了不同的问题。但是,多亏了这个评论,我现在已经成功解决了它。我很快会更新我的帖子。无论如何,感谢您为我解决问题! - Jordan Walker
使用文件加载器对我有效。 - Joel Davey

2

这是我的解决方案:

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,
                    },
                }
            ],
        },
    ]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接