Webpack样式加载器/ css-loader:url()路径分辨率不起作用

问题描述 投票:2回答:1

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

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

基本上,错误消息显示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中的错误(./node_modules/css-loader??ref--5-1!./node_modules/postcss-loader/src??ref--5-2!./node_modules/sass-装载机/ lib目录/ loader.js ??裁判 - !5-3 ./ SRC / main.scss)

找不到模块:错误:无法解析'C:\ Users \ ... \ src'中的'../fonts/open-iconic.eot'@ ./src/main.scss(./node_modules/css-装载机?? REF - !5-1 ./ node_modules / postcss装载机/ src目录??裁判 - !5-2 ./ node_modules /上海社会科学院装载机/ lib目录/ loader.js ??裁判 - 5-3! ./src/main.scss)7:106-141 7:172-207 @ ./src/main.scss @ ./src/index.js

我试过的:

我的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
    }
  };
};
javascript css webpack webpack-style-loader css-loader
1个回答
2
投票

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


  1. 首先,如果你同时使用postcss-loaderpostcss-import插件,并且css-loader,请关闭/删除postcss-import插件。您不需要多个工具来解决@import规则。如果加载器的顺序正确,这不是一个真正的问题,但你也可以删除它。
  2. sass-loader文档中,你可以read the following

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

  • 如果您只是生成CSS而不将其传递给css-loader,那么它必须与您的Web根目录相关。
  • 如果将生成的CSS传递给css-loader,则所有URL必须相对于entry-file(例如main.scss)。

更有可能你会被这第二个问题打乱。期望相对引用可以针对指定它们的.scss文件进行解析(比如常规的.css文件)。值得庆幸的是,这个问题有两个解决方案:

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

我决定关注子弹二,并在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 }
        }
      ]
    },

附注

  1. 我注意到Chrome的调试器中“无域”下的源映射路径会重复出现。如果有人弄清楚原因,请分享
  2. 请记住在package.json中包含以下副作用,因此在生产模式下发生的树抖动不会删除提取的css “sideEffects”:[“。css”,“。scss”],
© www.soinside.com 2019 - 2024. All rights reserved.