Webpack样式加载器/css加载器:url()路径解析不起作用

iyfamqjs  于 2022-11-13  发布在  Webpack
关注(0)|答案(7)|浏览(253)

有一些关于style-loadercss-loader的SO帖子,但尽管如此,我还没有能够找到解决我的问题的方法。
简而言之,如果在其他css文件中导入@importcss文件,并且导入的css包含具有相对路径的url(),则无法正确解析路径。
基本上,错误消息表明Webpack最终认为导入的css中的url()路径是相对于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');
}

错误:

在./src/main.scss中出现错误(./节点模块/css-loader??ref--5-1!./节点模块/postcss-loader/src??ref--5-2!./节点模块/sass-loader/lib/loader.js??ref--5-3!./src/main.scss)
找不到模块:错误:无法解析'C:\Users...\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)中的'../字体/打开图标. eot''C:\Users..\src'*@ ./src/main.scss @./src/index. js

我尝试过的方法:

  • 我已经尝试在样式加载器中使用convertToAbsoluteUrls标志
  • 我已尝试关闭所有源Map(mentioned in style-loader docs
    我的Webpack配置文件(加载程序位于底部):
const path = require('path');
const webpack = require('webpack'); // for webpack built-in plugins
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
// const WriteFilePlugin = require('write-file-webpack-plugin');
// const ManifestPlugin = require('webpack-manifest-plugin');
// const InlineManifestWebpackPlugin = require('inline-manifest-webpack-plugin');

// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

const PATHS = {
  // when using __dirname, resolve and join gives same result,
  // because __dirname is absolute path to directory of this file.
  // OK to use no slashes,
  // both resolve and join adds platform-specific separators by default
  src: path.resolve(__dirname, 'src'),
  dist: path.resolve(__dirname, 'dist'),
  build: path.resolve(__dirname, 'build'),
  test: path.resolve(__dirname, 'test')
};

const NAMES = {
  // JS FILES
  index: 'index',
  print: 'print',
  // Chrome Extension Development
  popup: 'popup',
  options: 'options',
  background: 'background',
  contentScript: 'contentScript',

  // FOLDERS
  assets: 'assets',
  utilities: 'utilities',
  images: 'images',
  fonts: 'fonts',
  include: 'include'
};

const FILE_PATHS = {
  // JS
  indexJs: `${path.join(PATHS.src, NAMES.index)}.js`,
  printJs: `${path.join(PATHS.src, NAMES.print)}.js`,
  // Chrome Extension Development
  popupJs: `${path.join(PATHS.src, NAMES.popup)}.js`,
  optionsJs: `${path.join(PATHS.src, NAMES.options)}.js`,
  backgroundJs: `${path.join(PATHS.src, NAMES.background)}.js`,
  contentScriptJs: `${path.join(
    PATHS.src,
    NAMES.include,
    NAMES.contentScript
  )}.js`,

  // HTML
  indexHtml: `${path.join(PATHS.src, NAMES.index)}.html`,
  printHtml: `${path.join(PATHS.src, NAMES.print)}.html`,
  // Chrome Extension Development
  popupHtml: `${path.join(PATHS.src, NAMES.popup)}.html`,
  optionsHtml: `${path.join(PATHS.src, NAMES.options)}.html`,
  backgroundHtml: `${path.join(PATHS.src, NAMES.background)}.html`
};

// Third-party (vendor) libraries to include
// const VENDORS = ['react', 'bootstrap', 'lodash', 'jQuery']; // Relative paths to node_modules

// Note: These are relative
const ASSETS = {
  images: path.join(NAMES.assets, NAMES.images),
  fonts: path.join(NAMES.assets, NAMES.fonts)
};

// CleanWebpackPlugin config
const pathsToClean = [PATHS.dist, PATHS.build];
const cleanOptions = {
  root: __dirname,
  exclude: ['shared.js'],
  verbose: true,
  dry: false
};

// CopyWebpackPlugin config
const copyPattern = [
  // {
  // from: NAMES.assets,
  // to: NAMES.assets
  // },
  // {
  // from: path.join(NAMES.include, 'contentScript.css')
  // },
  // {
  // from: 'manifest.json',
  // transform(content, copyPath) {
  // // generates the manifest file using the package.json informations
  // return Buffer.from(
  // JSON.stringify({
  // ...JSON.parse(content.toString())
  // // description: env.npm_package_description,
  // // version: env.npm_package_version
  // })
  // );
  // }
  // }
];
const copyOptions = {
  // ignore: ['*.js'],
  context: PATHS.src
};

module.exports = (env = {}) => {
  // webpack injects env variable, into webpack config.
  // perfect to check for production.
  // remember to specify --env.production in command
  // (if in production mode).
  const isProduction = env.production === true;

  return {
    entry: {
      index: FILE_PATHS.indexJs

      // Chrome Extension Development
      // popup: FILE_PATHS.popupJs,
      // contentScript: FILE_PATHS.contentScriptJs
      // options: FILE_PATHS.optionsJs,
      // background: FILE_PATHS.backgroundJs,

      // vendor: VENDORS
    },
    mode: isProduction ? 'production' : 'development',
    devtool: isProduction ? 'source-map' : 'inline-source-map',
    optimization: {
      splitChunks: {
        chunks: 'all'
      }
    },
    output: {
      filename: isProduction ? '[name].[chunkhash:8].js' : '[name].js',
      // chunkFilename determine name of non-entry chunk files,
      // for example dynamic imports in the app
      chunkFilename: isProduction ? '[name].[chunkhash:8].js' : '[name].js',
      path: PATHS.dist
    },
    plugins: [
      // new webpack.SourceMapDevToolPlugin({
      // filename: '[file].map',
      // exclude: ['vendor', 'runtime']
      // }),
      new webpack.DefinePlugin({
        // specifies environment variable for dependencies.
        // does not apply to browser runtime environment
        // (process.env is provisioned by Node)
        'process.env.NODE_ENV': isProduction ?
          JSON.stringify('production') :
          JSON.stringify('development')
      }),
      // new BundleAnalyzerPlugin(),
      new CleanWebpackPlugin(pathsToClean, cleanOptions),
      new MiniCssExtractPlugin({
        // Options similar to the same options in webpackOptions.output
        // both options are optional
        // does not work with Hot Module Replacement (HMR)
        // allows HMR in development (will only use this plugin in production)
        filename: isProduction ? '[name].[contenthash].css' : '[name].css',
        chunkFilename: isProduction ? '[id].[contenthash].css' : '[id].css'
      }),
      new webpack.HashedModuleIdsPlugin(),
      isProduction ?
      new UglifyJSPlugin({
        cache: true,
        parallel: true,
        sourceMap: true // set to true if you want JS source maps
      }) :
      () => {},
      new CopyWebpackPlugin(copyPattern, copyOptions),
      // new WriteFilePlugin(),
      new HtmlWebpackPlugin({
        template: FILE_PATHS.indexHtml,
        filename: `${NAMES.index}.html`
      })
      // new HtmlWebpackPlugin({
      // template: FILE_PATHS.popupHtml,
      // filename: `${NAMES.popup}.html`,
      // excludeChunks: [NAMES.contentScript]
      // In dev mode, chunks excluded vendor chunk (which holds CSS).
      // Above check fixes it.
      // }),
      // new HtmlWebpackPlugin({
      // filename: `${NAMES.contentScript}.html`,
      // excludeChunks: [NAMES.popup, 'runtime'] // Runtime only needed in one HTML
      // }),
      // new HtmlWebpackPlugin({
      // template: FILE_PATHS.optionsHtml,
      // filename: `${NAMES.options}.html`,
      // chunks: isProduction ? [NAMES.options] : ''
      // }),
      // new HtmlWebpackPlugin({
      // template: FILE_PATHS.backgroundHtml,
      // filename: `${NAMES.background}.html`,
      // chunks: isProduction ? [NAMES.background] : ''
      // }),
      // no need for CSS minimization here <-- Done by PostCSS (cssnano)
      // new InlineManifestWebpackPlugin(),
      // new ManifestPlugin({fileName: 'webpack-manifest.json'}),
    ],
    module: {
      rules: [{
          test: /\.js$/,
          exclude: /(node_modules|bower_components)/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env']
            }
          }
        },
        {
          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
              }
            },
            {
              loader: 'postcss-loader',
              options: {
                config: {
                  ctx: {
                    cssnext: {},
                    cssnano: {},
                    autoprefixer: {}
                  }
                },
                sourceMap: true
              }
            },
            {
              // compiles Sass to CSS
              loader: 'sass-loader',
              options: {
                sourceMap: true
              }
            }
          ]
        },
        {
          test: /\.(png|svg|jpg|gif)$/,
          use: [{
            loader: 'file-loader',
            options: {
              name: '[name].[hash:4].[ext]',
              outputPath: ASSETS.images
            }
          }]
        },
        {
          test: /\.(woff|woff2|eot|ttf|otf)$/,
          use: [{
            loader: 'file-loader',
            options: {
              name: '[name].[hash:4].[ext]',
              outputPath: ASSETS.fonts
            }
          }]
        },
        {
          test: /\.(csv|tsv)$/,
          use: ['csv-loader']
        },
        {
          test: /\.xml$/,
          use: ['xml-loader']
        },
        {
          test: /\.(html)$/,
          use: {
            loader: 'html-loader',
            options: {
              interpolate: 'require',
              minimize: true
            }
          }
        }
        // {
        // test: /\.tsx?$/,
        // exclude: /(node_modules|bower_components)/,
        // use: 'ts-loader'
        // }
      ]
    },
    devServer: {
      // contentBase: path.join(__dirname, 'dist'),
      contentBase: PATHS.dist,
      compress: false,
      port: 8080,
      open: false
    }
  };
};
oaxa6hgo

oaxa6hgo1#

我花了大约5天的工作来理解这个webpack是如何工作的。老实说,我可以说,这是我真的不明白为什么它们是“事实上”的工具的时刻之一。我不明白它有多难,只是让配置文件的工作,因为它应该,在大口地花了我1个小时做同样的事情。
我的问题是所有的网址()个规则(包括字体和图像)被css-loader加载为[object Module],它们被file-loader导出,但从未被加载,所以如果我在css-loader中添加?url=false,它就不会复制文件并导出它们。我不得不说这完全是一个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
        }),
    ]
};
uz75evzq

uz75evzq2#

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

{
  loader: 'css-loader',
  options: {
    ...
    url: false,
  }
},
r1wp621o

r1wp621o3#

我自己能解决这个问题。如果它能帮助别人在未来,请找到下面的解决方案。
1.首先,如果您同时使用postcss-loaderpostcss-import插件,并且使用css-loader,请关闭/删除postcss-import插件。您不需要多个工具来解析@import规则。如果加载程序的顺序正确,这实际上不是一个问题,但您最好删除它。
1.在sass-loader文档中,您可以阅读以下内容:
由于Sass/libsass不提供url重写,因此所有链接的资源都必须相对于输出。

  • 如果您只是生成CSS而不将其传递给css-loader,则它必须相对于您的web根目录。
  • 如果您将生成的CSS传递给css加载器,则所有url必须相对于入口文件(例如main.scss)。

第二个问题很可能会干扰您的工作。人们很自然地会期望相对引用在指定它们的.scss文件中被解析(就像在常规的.css文件中一样)。幸运的是,有两种解决方案可以解决这个问题:

  • 使用resolve-url-loader添加缺少的url重写。将其放置在加载器链中的sass-loader之前。
  • 库作者通常会提供一个变量来修改资产路径。例如bootstrap-sass有一个$icon-font-path。看看这个工作的bootstrap示例。

我决定按照第二点,在 Webpack 配置中的sass-loader之上添加resolve-url-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 }
        }
      ]
    },

快速笔记

1.我注意到Chrome的调试器中“无域”下的源代码Map路径是重复的。如果有人知道原因,请分享
1.记住在package.json中包含以下副作用,这样在生产模式下发生的树抖动不会删除提取的css
“副作用”:[“.css”、“.scss”]、

nhjlsmyf

nhjlsmyf4#

IE与新URL()不兼容

vfwfrxfs

vfwfrxfs5#

如果有人在使用Webpack 5时遇到了这个问题,并试图从css-loader 5升级到css-loader 6,您可能需要检查this issue,其中的海报有一个与OP类似的问题:
在css-loader 5.2.7中,输入手写笔中的图像被嵌入为输出CSS中的数据- URL。而在css-loader 6中,图像被移动到输出目录中。
有关如何升级到css-loader 6的信息,请参见Notes here-要点如下:
启用esModules选项时,不建议使用~...并且可以从代码中删除

file-loader和url-loader已过时,请在资产模块上迁移,因为v6 css-loader正在生成新的URL(...)语法,它默认启用内置资产模块,即类型:所有url()的'asset'
因此,我做了以下工作:
1.已删除.scss文件中的所有“~”
也就是说

$font-path: "~/src/fonts" !default;

变成了

$font-path: "/src/fonts" !default;

1.我还完全删除了“文件加载器”模块。
所有NPM软件包现在都是最新的,CSS中的URL工作正常。

ss2ws0br

ss2ws0br6#

我的问题是,webpack 5改变了一切与“资产模块”显然和打破文件加载器。
官方文件对此解释得很清楚:https://webpack.js.org/guides/asset-modules/
TL;DR您可以通过将type: 'javascript/auto'添加到规则/加载程序来修复它

{
                test: /\.(eot|ttf|woff2?)($|\?)/i,
                loader: 'file-loader',
                options: {
                    name: '[name]-[sha1:hash:hex:10].[ext]',
                    esModule: false,
                },
                type: 'javascript/auto'
            },
vwkv1x7d

vwkv1x7d7#

在Webpack 5中,应避免使用raw-loaderfile-loader,而应使用asset/sourceasset/resource

{
    test: /\.txt$/,
    type: 'asset/source',
  },
  {
    test: /\.png$/,
    type: 'asset/resource',
  },

您可以在以下位置阅读有关资产的更多信息:https://webpack.js.org/guides/asset-modules/。它描述了对于Webpack4和更早版本,应该如何使用file-loader,对于Webpack5和更高版本,应该如何使用资产模块。

相关问题