url-loader / file-loader 使用 webpack 破坏 css 输出中的相对路径

Posted

技术标签:

【中文标题】url-loader / file-loader 使用 webpack 破坏 css 输出中的相对路径【英文标题】:url-loader / file-loader breaking relative paths in css output using webpack 【发布时间】:2019-05-16 04:24:48 【问题描述】:

我正在使用带有一些插件和加载器的 webpack 来获取我的 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 以便这些文件可以被 webpack 解析并且其中的任何图像都可以被 url-loader 处理:

index.js

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

style.scss 使用相对路径在 body 上设置背景图片:

style.scss

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

index.html 只显示一个图像,也使用相对路径:

index.html

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

我使用 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 还是在开发中注入它。

我尝试过使用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());

【问题讨论】:

【参考方案1】:

已解决 感谢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
            
        
    ]
,
...

要在开发模式下使用样式加载器而不是像我原来的 webpack.config.js 中那样使用 MiniCssExtractPlugin,您必须有条件地添加选项,因为样式加载器不接受 publicPath 选项。我在 webpack.config.js 的底部这样做了:

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

然后确保规则数组中的第一个对象是用于 scss。有条件地添加这个有点乱,但现在可以了。

【讨论】:

【参考方案2】:

在这上面花了很多时间! 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 文件中找到图像,因为它在同一个文件夹中查找它。 (未找到模块:错误:无法解析“/Users/jlwalker/demo/src/assets/scss”中的“./background.jpg”)。它还使 dist/ 输出文件中的路径成为绝对路径,而理想情况下我更喜欢使用相对路径。 resolve-url-loader 有什么问题。你是在 sass-loader 上面加的吗? 据我所知,resolve-url-loader 允许您在 sass 部分中使用与这些部分相关的路径,而不是相对于导入这些部分的 sass 文件。我有一个不同的问题。但是由于comment,我现在已经设法让它工作了。将很快更新我的帖子。无论如何,感谢您为我调查! 使用文件加载器为我工作【参考方案4】:

这是我的解决方案:

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

【讨论】:

以上是关于url-loader / file-loader 使用 webpack 破坏 css 输出中的相对路径的主要内容,如果未能解决你的问题,请参考以下文章

url-loader / file-loader 使用 webpack 破坏 css 输出中的相对路径

Webpack url-loader 或 file-loader 不起作用反应应用程序

Webpack 使用url-loader和file-loader打包资源文件

vue file-loader vs url-loader

3.2.3 webpack图片等资源的处理 file-loader|url-loader|img-loader

webpack学习笔记-2-file-loader 和 url-loader