url-loader / file-loader使用Webpack打破CSS输出中的相对路径

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');
}


花了很长时间!缺少以下publicPath设置!

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