url-loader / file-loader разрывая относительные пути в выводе css с помощью веб-пакета

Я использую веб-пакет с некоторыми плагинами и загрузчиками, чтобы взять мою папку src / и создать папку dist /. url-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, чтобы эти файлы можно было проанализировать через веб-пакет, а любые изображения в них можно обработать с помощью url-loader:

index.js

import '../../index.html';
import '../scss/style.scss';

style.scss устанавливает фоновое изображение на теле, используя относительный путь:

style.scss

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

index.html просто отображает изображение, также используя относительный путь:

index.html

<img src="./assets/img/pic.jpg" alt="pic">

Я использую HtmlWebpackPlugin для копирования через мои html-файлы, поскольку он позволяет мне указывать, какие фрагменты автоматически включать в качестве тегов сценария. Для css я либо внедряю его в html-файлы с помощью style-loader в разработке, либо извлекаю его в собственный файл при производстве с помощью MiniCssExtractPlugin.

Однако, когда webpack анализирует index.html и style.scss, относительные пути заменяются на "assets/img/pic.jpg" и "assets/img/backgorund.jpg" соответственно. Это не нарушает путь в index.html, так как это фактически тот же относительный путь, но это явно проблема для style.css. Как мне остановить url-loader от изменения относительных путей или просто сгенерировать правильные? Также обратите внимание, что, когда CSS внедряется в HTML с помощью style-loader в разработке, путь работает, так как он тогда относительно файла HTML. В идеале веб-пакет должен иметь возможность генерировать правильный относительный путь в зависимости от того, извлекаю ли я css в производственном процессе или внедряю его в процессе разработки.

Я попытался использовать resol-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());
}

2 ответа

РЕШИТЬ

Решено благодаря этому сообщению на github: https://github.com/webpack-contrib/mini-css-extract-plugin/issues/44.

Просто добавьте параметр 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
            }
        }
    ]
},
...

Чтобы использовать загрузчик стилей в режиме разработки вместо MiniCssExtractPlugin, как в моем оригинальном webpack.config.js, вам нужно будет добавить опцию в условно, потому что загрузчик стилей не принимает опцию publicPath. Я сделал это в нижней части webpack.config.js примерно так:

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

Затем убедитесь, что первый объект в массиве правил предназначен для scss. Какой-то грязный способ добавить это условно, но пока подойдет.

Потратил на это agaes! настройка общественного пути ниже отсутствует!

 output: {

// publicPath: '/'},

Попробуйте добавить опцию 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');
}

Вот мое решение:

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,
                    },
                }
            ],
        },
    ]
Другие вопросы по тегам