Webpack style-loader / css-loader: url() 路径解析不起作用

Mag*_*nus 12 javascript css webpack webpack-style-loader css-loader

有一些关于style-loaderand 的SO 帖子css-loader,但尽管如此,我还是无法找到解决我的问题的方法。

简而言之,当我@import css在其他css文件中归档时,导入的css包含url()具有相对路径的 s 时,路径解析不正确。

基本上,错误消息表明 Webpack 最终认为url()导入的 css 中的路径是相对于src(主入口点),而不是相对于css它导入的文件:

// css-one.scss
@import "./assets/open-iconic-master/font/css/open-iconic-bootstrap.css";

// open-iconic-bootstrap.css
@font-face {
    src: url('../fonts/open-iconic.eot');
}
Run Code Online (Sandbox Code Playgroud)

错误:

./src/main.scss 中的错误 (./node_modules/css-loader??ref--5-1!./node_modules/postcss-loader/src??ref--5-2!./node_modules/sass- loader/lib/loader.js??ref--5-3!./src/main.scss)

模块未找到:错误:无法解析'../fonts/open-iconic.eot''C:\用户\ ... \ src'中 @ ./src/main.scss (./node_modules/css- loader??ref--5-1!./node_modules/postcss-loader/src??ref--5-2!./node_modules/sass-loader/lib/loader.js??ref--5-3! ./src/main.scss) 7:106-141 7:172-207 @ ./src/main.scss @ ./src/index.js

我试过的:

我的 Webpack 配置文件(加载程序在底部):

// css-one.scss
@import "./assets/open-iconic-master/font/css/open-iconic-bootstrap.css";

// open-iconic-bootstrap.css
@font-face {
    src: url('../fonts/open-iconic.eot');
}
Run Code Online (Sandbox Code Playgroud)

小智 12

我花了大约 5 天的时间来理解这个 webpack 混乱是如何工作的。老实说,我可以说这是我真的不明白为什么它们是目前“事实上的”工具之一。我无法理解仅仅让配置文件正常工作有多困难,在 gulp 中我花了 1 小时来做同样的事情。

我的问题是所有 url() 规则(包括字体和图像)都由 css-loader 作为 [object Module] 加载,并且它们由 file-loader 导出但从未加载,所以如果我添加 ?url=false 到css-loader 它从不复制文件并导出它们。我不得不说这完全是一个 PITA,但我让它工作了,我希望它适用于世界上的其他人,这是用 webpack 4 制作的。

const webpack = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const ImageminPlugin = require('imagemin-webpack-plugin').default;
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
    entry: "./src/index.js",
    mode: "development",
    module: {
        rules: [
        {
            test: /\.(js|jsx)$/,
            exclude: /(node_modules|bower_components)/,
            loader: "babel-loader",
            options: { presets: ["@babel/env"] }
        },
        {
            test: /\.(gif|png|jpe?g|svg)$/i,
            use: [
            {
                loader: 'image-webpack-loader',
                options: {
                    mozjpeg: {
                        progressive: true,
                        quality: 65
                    },

                    optipng: {
                        enabled: false,
                    },
                    pngquant: {
                        quality: [0.65, 0.90],
                        speed: 4
                    },
                    gifsicle: {
                        interlaced: false,
                    },

                    webp: {
                        quality: 75
                    },
                }
            },
            {
                loader: 'file-loader',
                options:{
                    name: '[name].[ext]',
                    outputPath: 'images/',
                    publicPath: 'images/'
                }
            },
            'url-loader?limit=100000'
            ],
        },
        {
            test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
            use: [
            {
                loader: 'file-loader',
                options: {
                    name: '[name].[ext]',
                    outputPath: 'fonts/'
                }
            }
            ]
        },
        {
            test: /\.s[ac]ss$/i,
            use: [
            MiniCssExtractPlugin.loader,
            { loader: 'css-loader?url=false'},
            { loader: 'sass-loader', options: { sourceMap: true } }
            ],
        },
        ]
    },
    resolve: { extensions: ["*", ".js", ".jsx"] },
    output: {
        path: path.resolve(__dirname, "dist/"),
        publicPath: "",
        filename: "bundle.js"
    },
    devServer: {
        contentBase: path.join(__dirname, "dist/"),
        port: 3000,
        publicPath: "http://localhost:3000/dist/",
        hotOnly: true
    },
    plugins: [ new MiniCssExtractPlugin(),
    new CopyPlugin([{ from: 'src/images/', to: 'images/' }]),
    new CopyPlugin([{ from: 'src/fonts/', to: 'fonts/' }]),
    new ImageminPlugin({ test: /\.(jpe?g|png|gif|svg)$/i }),
    new HtmlWebpackPlugin({
        hash: true,
        template: './src/index.html',
            filename: './index.html' //relative to root of the application
        }),
    ]
};
Run Code Online (Sandbox Code Playgroud)


Mag*_*nus 10

我能够自己解决问题。如果将来可以帮助其他人,请在下面找到解决方案。


  1. 首先,如果你同时使用postcss-loaderpostcss-import开发插件,并且css-loader,关闭/删除postcss-import插件。您不需要多个解决@import规则的工具。如果加载程序的顺序正确,这并不是真正的问题,但您不妨将其删除。
  2. sass-loader文档中,您可以阅读以下内容

由于 Sass/libsass 不提供 url 重写,因此所有链接的资产都必须与输出相关。

  • 如果您只是生成 CSS 而不将其传递给 css-loader,则它必须与您的 Web 根目录相关。

  • 如果您将生成的 CSS 传递给 css-loader,则所有 url 都必须相对于入口文件(例如 main.scss)。

您更有可能被第二个问题打扰。很自然地期望相对引用针对指定它们的 .scss 文件(如在常规 .css 文件中)进行解析。幸运的是,这个问题有两种解决方案:

  • 使用resolve-url-loader 添加丢失的url 重写。将它放在 loader 链中的 sass-loader 之前。

  • 库作者通常提供一个变量来修改资产路径。例如 bootstrap-sass 有一个 $icon-font-path。查看这个工作引导程序示例。

我决定遵循第二点,并在Webpack配置中添加resolve-url-loader上面sass-loader内容。它现在按预期工作。

我的最终 Webpack 配置(目前)如下所示:

    {
      test: /\.s?[ac]ss$/,
      exclude: /node_modules/,
      use: [
        isProduction
          ? MiniCssExtractPlugin.loader
          : {
              // creates style nodes from JS strings
              loader: 'style-loader',
              options: {
                sourceMap: true,
                // convertToAbsoluteUrls: true
              }
            },
        {
          // CSS to CommonJS (resolves CSS imports into exported CSS strings)
          loader: 'css-loader',
          options: {
            sourceMap: true,
            importLoaders: 2
            // url: false,
            // import: false
          }
        },
        {
          loader: 'postcss-loader',
          options: {
            config: {
              ctx: {
                cssnext: {},
                cssnano: {},
                autoprefixer: {}
              }
            },
            sourceMap: true
          }
        },
        {
          loader: 'resolve-url-loader',
          options: {
            attempts: 1,
            sourceMap: true
          }
        },
        {
          // compiles Sass to CSS
          loader: 'sass-loader',
          options: { sourceMap: true }
        }
      ]
    },
Run Code Online (Sandbox Code Playgroud)

旁注

  1. 我注意到 Chrome 调试器中“无域”下的源映射路径是重复的。如果有人知道原因,请分享
  2. 请记住在 中包含以下副作用package.json,因此在生产模式下发生的摇树不会删除提取的 css

    "sideEffects": [ " .css", " .scss"],


Tac*_*irl 7

url()顺便说一句,您可以关闭规则处理。我不知道为什么这是默认行为。

{
  loader: 'css-loader',
  options: {
    ...
    url: false,
  }
},
Run Code Online (Sandbox Code Playgroud)