css-loader

免责声明 css-loader是一个由社区成员维护的第三方包,它可能不具备与 webpack 相同的支持、安全策略或许可证,并且不受 webpack 维护。

npm node tests coverage discussion size

css-loader@importurl() 解析为 import/require() 语句并处理它们。

入门

[!警告]

要使用最新版本的 css-loader,需要 webpack@5

首先,你需要安装 css-loader

npm install --save-dev css-loader

yarn add -D css-loader

pnpm add -D css-loader

然后,将加载器添加到你的 webpack 配置中。例如:

file.js

import * as css from "file.css";

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

最后,使用你通常使用的方法运行 webpack(例如,通过 CLI 或 npm 脚本)。

如果你需要将 CSS 提取到单独的文件中(即不将 CSS 存储在 JS 模块中),请考虑使用推荐示例

选项

url

类型

type url =
  | boolean
  | {
      filter: (url: string, resourcePath: string) => boolean;
    };

默认值:true

启用或禁用对 CSS 函数 urlimage-set 的处理。

  • 如果设置为 falsecss-loader 将不会解析 urlimage-set 中指定的任何路径。
  • 你也可以传入一个函数,根据资源路径动态控制此行为。

从版本 4.0.0 开始,绝对路径将根据服务器根目录进行解析。

解析示例

url(image.png) => require('./image.png')
url('image.png') => require('./image.png')
url(./image.png) => require('./image.png')
url('./image.png') => require('./image.png')
url('http://dontwritehorriblecode.com/2112.png') => require('http://dontwritehorriblecode.com/2112.png')
image-set(url('image2x.png') 1x, url('image1x.png') 2x) => require('./image1x.png') and require('./image2x.png')

要从 node_modules 路径(包括 resolve.modules)或 alias 导入资源,请在其前面加上 ~

url(~module/image.png) => require('module/image.png')
url('~module/image.png') => require('module/image.png')
url(~aliasDirectory/image.png) => require('otherDirectory/image.png')

boolean

启用/禁用 url() 解析。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          url: true,
        },
      },
    ],
  },
};

object

允许过滤 url() 值。

任何被过滤的 url() 都将不会被解析(保留在代码中,原样不动)。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          url: {
            filter: (url, resourcePath) => {
              // resourcePath - path to css file

              // Don't handle `img.png` urls
              if (url.includes("img.png")) {
                return false;
              }

              // Don't handle images under root-relative /external_images/
              if (/^\/external_images\//.test(url)) {
                return false;
              }

              return true;
            },
          },
        },
      },
    ],
  },
};

import

类型

type importFn =
  | boolean
  | {
      filter: (
        url: string,
        media: string,
        resourcePath: string,
        supports?: string,
        layer?: string,
      ) => boolean;
    };

默认值:true

允许你启用或禁用 @import 规则的处理。

控制 @import 语句如何解析。

@import 中的绝对 URL 将在运行时代码中移动。

解析示例

@import 'style.css' => require('./style.css')
@import url(style.css) => require('./style.css')
@import url('style.css') => require('./style.css')
@import './style.css' => require('./style.css')
@import url(./style.css) => require('./style.css')
@import url('./style.css') => require('./style.css')
@import url('http://dontwritehorriblecode.com/style.css') => @import url('http://dontwritehorriblecode.com/style.css') in runtime

要从 node_modules 路径(包括 resolve.modules)或 alias 导入样式,请在其前面加上 ~

@import url(~module/style.css) => require('module/style.css')
@import url('~module/style.css') => require('module/style.css')
@import url(~aliasDirectory/style.css) => require('otherDirectory/style.css')

boolean

启用/禁用 @import 解析。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          import: true,
        },
      },
    ],
  },
};

object

filter

类型

type filter = (url: string, media: string, resourcePath: string) => boolean;

默认值:undefined

允许过滤 @import

任何被过滤的 @import 都将不会被解析(保留在代码中,原样不动)。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          import: {
            filter: (url, media, resourcePath) => {
              // resourcePath - path to css file

              // Don't handle `style.css` import
              if (url.includes("style.css")) {
                return false;
              }

              return true;
            },
          },
        },
      },
    ],
  },
};

modules

类型

type modules =
  | boolean
  | "local"
  | "global"
  | "pure"
  | "icss"
  | {
      auto: boolean | regExp | ((resourcePath: string) => boolean);
      mode:
        | "local"
        | "global"
        | "pure"
        | "icss"
        | ((resourcePath) => "local" | "global" | "pure" | "icss");
      localIdentName: string;
      localIdentContext: string;
      localIdentHashSalt: string;
      localIdentHashFunction: string;
      localIdentHashDigest: string;
      localIdentRegExp: string | regExp;
      getLocalIdent: (
        context: LoaderContext,
        localIdentName: string,
        localName: string,
      ) => string;
      namedExport: boolean;
      exportGlobals: boolean;
      exportLocalsConvention:
        | "as-is"
        | "camel-case"
        | "camel-case-only"
        | "dashes"
        | "dashes-only"
        | ((name: string) => string);
      exportOnlyLocals: boolean;
      getJSON: ({
        resourcePath,
        imports,
        exports,
        replacements,
      }: {
        resourcePath: string;
        imports: object[];
        exports: object[];
        replacements: object[];
      }) => Promise<void> | void;
    };

默认值:undefined

允许你启用或禁用 CSS Modules 或 ICSS 并配置它们

  • undefined: 为所有匹配 /\.module\.\w+$/i.test(filename)/\.icss\.\w+$/i.test(filename) 正则表达式的文件启用 CSS 模块。
  • true: 为所有文件启用 CSS 模块。
  • false: 为所有文件禁用 CSS 模块。
  • string: 为所有文件禁用 CSS 模块并设置 mode 选项(有关详细信息,请参见 mode)。
  • object: 为所有文件启用 CSS 模块,除非提供了 modules.auto 选项。否则 modules.auto 选项将决定是否为 CSS 模块(有关详细信息,请参见 auto)。

modules 选项启用/禁用 CSS Modules 规范并配置其行为。

modules: false 可以提高性能,因为我们避免了解析 CSS Modules 功能,这对于使用原生 CSS 或其他技术的开发人员很有用。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: true,
        },
      },
    ],
  },
};

特性

作用域
  • 使用 local 值需要你指定 :global 类。
  • 使用 global 值需要你指定 :local 类。
  • 使用 pure 值要求选择器必须包含至少一个本地类或 ID。

你可以在这里找到关于作用域模块的更多信息。

使用 CSS 模块,样式是本地作用域的,防止与全局样式冲突。

使用 :local(.className) 在本地作用域中声明一个 className。本地标识符由模块导出。

  • 使用 :local(不带括号),此选择器的本地模式可以被切换为 on
  • :global(.className) 符号可用于声明显式的全局选择器。
  • 使用 :global(不带括号),此选择器的全局模式可以被切换为 on

加载器将本地选择器替换为唯一的、带作用域的标识符。所选的唯一标识符由模块导出。

:local(.className) {
  background: red;
}
:local .className {
  color: green;
}
:local(.className .subClass) {
  color: green;
}
:local .className .subClass :global(.global-class-name) {
  color: blue;
}

输出(示例)

._23_aKvs-b8bW2Vg3fwHozO {
  background: red;
}
._23_aKvs-b8bW2Vg3fwHozO {
  color: green;
}
._23_aKvs-b8bW2Vg3fwHozO ._13LGdX8RMStbBE9w-t0gZ1 {
  color: green;
}
._23_aKvs-b8bW2Vg3fwHozO ._13LGdX8RMStbBE9w-t0gZ1 .global-class-name {
  color: blue;
}

[!注意]

标识符被导出

exports.locals = {
  className: "_23_aKvs-b8bW2Vg3fwHozO",
  subClass: "_13LGdX8RMStbBE9w-t0gZ1",
};

建议对本地选择器使用 CamelCase 命名,因为它简化了在导入的 JS 模块中的使用。

虽然你可以使用 :local(#someId),但不推荐这样做。对于模块化样式,优先使用类而不是 ID。

组合

在声明本地类名时,你可以将其组合自一个或多个其他本地类名。

:local(.className) {
  background: red;
  color: yellow;
}

:local(.subClass) {
  composes: className;
  background: blue;
}

这不会改变最终的 CSS 输出,但生成的 subClass 将在其导出中包含两个类名。

exports.locals = {
  className: "_23_aKvs-b8bW2Vg3fwHozO",
  subClass: "_13LGdX8RMStbBE9w-t0gZ1 _23_aKvs-b8bW2Vg3fwHozO",
};
._23_aKvs-b8bW2Vg3fwHozO {
  background: red;
  color: yellow;
}

._13LGdX8RMStbBE9w-t0gZ1 {
  background: blue;
}
导入

从另一个模块导入本地类名。

[!注意]

强烈建议在导入文件时包含文件扩展名,因为可以导入任何扩展名的文件,并且事先不知道要使用哪个文件。

:local(.continueButton) {
  composes: button from "library/button.css";
  background: red;
}
:local(.nameEdit) {
  composes: edit highlight from "./edit.css";
  background: red;
}

要从多个模块导入,请使用多个 composes: 规则。

:local(.className) {
  composes:
    edit highlight from "./edit.css",
    button from "module/button.css",
    classFromThisModule;
  background: red;
}

:local(.className) {
  composes: edit highlight from "./edit.css";
  composes: button from "module/button.css";
  composes: classFromThisModule;
  background: red;
}

你可以使用 @value 来指定可在整个文档中重复使用的值。

我们建议遵循命名约定

  • 值使用 v- 前缀
  • 选择器使用 s- 前缀
  • 媒体规则使用 m- 前缀。
@value v-primary: #BF4040;
@value s-black: black-selector;
@value m-large: (min-width: 960px);

.header {
  color: v-primary;
  padding: 0 10px;
}

.s-black {
  color: black;
}

@media m-large {
  .header {
    padding: 0 20px;
  }
}

boolean

启用 CSS 模块 功能。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: true,
        },
      },
    ],
  },
};

string

启用 CSS 模块 功能并设置 mode

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          // Using `local` value has same effect like using `modules: true`
          modules: "global",
        },
      },
    ],
  },
};

object

启用 CSS 模块 功能并通过 options 配置其行为。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            mode: "local",
            auto: true,
            exportGlobals: true,
            localIdentName: "[path][name]__[local]--[hash:base64:5]",
            localIdentContext: path.resolve(__dirname, "src"),
            localIdentHashSalt: "my-custom-hash",
            namedExport: true,
            exportLocalsConvention: "as-is",
            exportOnlyLocals: false,
            getJSON: ({ resourcePath, imports, exports, replacements }) => {},
          },
        },
      },
    ],
  },
};
auto

类型

type auto =
  | boolean
  | regExp
  | ((
      resourcePath: string,
      resourceQuery: string,
      resourceFragment: string,
    ) => boolean);

默认值:undefined

modules 选项是一个对象时,允许根据文件名、查询字符串或片段自动启用 CSS 模块或 ICSS。

可能的值

  • undefined: 为所有文件启用 CSS 模块。
  • true: 为所有匹配 /\.module\.\w+$/i.test(filename)/\.icss\.\w+$/i.test(filename) 正则表达式的文件启用 CSS 模块。
  • false: 为所有文件禁用 CSS 模块。
  • RegExp: 为所有匹配 /RegExp/i.test(filename) 正则表达式的文件启用 CSS 模块。
  • function: 根据满足你过滤函数检查的文件名启用 CSS 模块。
boolean

可能的值

  • true: 启用 CSS 模块或可互操作 CSS (ICSS) 格式,将 modules.mode 选项设置为 local 值,适用于所有满足 /\.module(s)?\.\w+$/i.test(filename) 条件的文件,或将 modules.mode 选项设置为 icss 值,适用于所有满足 /\.icss\.\w+$/i.test(filename) 条件的文件。
  • false: 对所有文件禁用基于文件名的 CSS 模块或 ICSS 格式。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            auto: true,
          },
        },
      },
    ],
  },
};
RegExp

根据满足你的正则表达式检查的文件名启用 CSS 模块。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            auto: /\.custom-module\.\w+$/i,
          },
        },
      },
    ],
  },
};
function

根据满足你过滤函数检查的文件名、查询字符串或片段启用 CSS 模块。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            auto: (resourcePath, resourceQuery, resourceFragment) => {
              return resourcePath.endsWith(".custom-module.css");
            },
          },
        },
      },
    ],
  },
};
mode

类型

type mode =
  | "local"
  | "global"
  | "pure"
  | "icss"
  | ((
      resourcePath: string,
      resourceQuery: string,
      resourceFragment: string,
    ) => "local" | "global" | "pure" | "icss");

默认值: 'local'

设置 mode 选项。当你想使用 local 模式时,可以省略该值。

控制应用于输入样式的编译级别。

  • localglobalpure 处理 classid 的作用域以及 @value 值。
  • icss 将只编译低级别的 可互操作 CSS (ICSS) 格式,用于声明 CSS 与其他语言之间的 :import:export 依赖。

ICSS 是 CSS 模块支持的基础,并为其他工具提供了实现其自身 CSS 模块变体的低级语法。

string

可能的值 - local, global, pure, 和 icss

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            mode: "global",
          },
        },
      },
    ],
  },
};
function

允许根据文件名、查询字符串或片段为 mode 选项设置不同的值。可能的返回值 - local, global, pureicss

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            // Callback must return "local", "global", or "pure" values
            mode: (resourcePath, resourceQuery, resourceFragment) => {
              if (/pure.css$/i.test(resourcePath)) {
                return "pure";
              }

              if (/global.css$/i.test(resourcePath)) {
                return "global";
              }

              return "local";
            },
          },
        },
      },
    ],
  },
};
localIdentName

类型

type localIdentName = string;

默认值: '[hash:base64]'

允许配置生成的本地标识符名称。

有关选项的更多信息,请参阅

支持的模板字符串

  • [name] 资源的 basename
  • [folder] 资源相对于 compiler.context 选项或 modules.localIdentContext 选项的文件夹。
  • [path] 资源相对于 compiler.context 选项或 modules.localIdentContext 选项的路径。
  • [file] - 文件名和路径。
  • [ext] - 带前导 . 的扩展名。
  • [hash] - 字符串的哈希值,根据 localIdentHashSaltlocalIdentHashFunctionlocalIdentHashDigestlocalIdentHashDigestLengthlocalIdentContextresourcePathexportName 生成
  • [<hashFunction>:hash:<hashDigest>:<hashDigestLength>] - 带有哈希设置的哈希值。
  • [local] - 原始类。

建议

  • 开发环境使用 '[path][name]__[local]'
  • 生产环境使用 '[hash:base64]'

[local] 占位符包含原始类。

注意: 所有保留字符 (<>:"/\|?*) 和控制文件系统字符([local] 占位符中的字符除外)将被转换为 -

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            localIdentName: "[path][name]__[local]--[hash:base64:5]",
          },
        },
      },
    ],
  },
};
localIdentContext

类型

type localIdentContex = string;

默认值:compiler.context

允许重新定义本地标识符名称的基本加载器上下文。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            localIdentContext: path.resolve(__dirname, "src"),
          },
        },
      },
    ],
  },
};
localIdentHashSalt

类型

type localIdentHashSalt = string;

默认值:undefined

允许添加自定义哈希值以生成更唯一的类。

有关更多信息,请参阅 output.hashSalt

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            localIdentHashSalt: "hash",
          },
        },
      },
    ],
  },
};
localIdentHashFunction

类型

type localIdentHashFunction = string;

默认值: md4

允许指定用于生成类的哈希函数。

有关更多信息,请参阅 output.hashFunction

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            localIdentHashFunction: "md4",
          },
        },
      },
    ],
  },
};
localIdentHashDigest

类型

type localIdentHashDigest = string;

默认值: hex

允许指定用于生成类的哈希摘要。

有关更多信息,请参阅 output.hashDigest

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            localIdentHashDigest: "base64",
          },
        },
      },
    ],
  },
};
localIdentHashDigestLength

类型

type localIdentHashDigestLength = number;

默认值: 20

允许指定用于生成类的哈希摘要长度。

有关更多信息,请参阅 output.hashDigestLength

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            localIdentHashDigestLength: 5,
          },
        },
      },
    ],
  },
};
hashStrategy

类型: 'resource-path-and-local-name' | 'minimal-subset' 默认值: 'resource-path-and-local-name'

计算哈希时是否应使用本地名称。

  • 'resource-path-and-local-name': 在计算哈希时同时使用资源路径和本地名称。模块中的每个标识符都会始终获得自己的哈希摘要。
  • 'minimal-subset': 自动检测是否可以在哈希计算中省略标识符名称。使用此值可以优化输出,以获得更好的 GZIP 或 Brotli 压缩效果。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            hashStrategy: "minimal-subset",
          },
        },
      },
    ],
  },
};
localIdentRegExp

类型

type localIdentRegExp = string | RegExp;

默认值:undefined

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            localIdentRegExp: /page-(.*)\.css/i,
          },
        },
      },
    ],
  },
};
getLocalIdent

类型

type getLocalIdent = (
  context: LoaderContext,
  localIdentName: string,
  localName: string,
) => string;

默认值:undefined

允许指定一个函数来生成类名。

默认情况下,我们使用内置函数生成类名。

如果你的自定义函数返回 nullundefined,则使用内置生成器作为 fallback

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            getLocalIdent: (context, localIdentName, localName, options) => {
              return "whatever_random_class_name";
            },
          },
        },
      },
    ],
  },
};
namedExport

类型

type namedExport = boolean;

默认值: 取决于 esModule 选项的值。如果 esModule 选项的值为 true,则 namedExport 默认为 true;否则,默认为 false

启用或禁用 ES 模块对本地变量的命名导出。

[!警告]

namedExporttrue 时,不能直接使用 default 类名,因为 default 是 ECMAScript 模块中的保留关键字。它会自动重命名为 _default

styles.css

.foo-baz {
  color: red;
}
.bar {
  color: blue;
}
.default {
  color: green;
}

index.js

import * as styles from "./styles.css";

// If using `exportLocalsConvention: "as-is"` (default value):
console.log(styles["foo-baz"], styles.bar);

// If using `exportLocalsConvention: "camel-case-only"`:
console.log(styles.fooBaz, styles.bar);

// For the `default` classname
console.log(styles["_default"]);

你可以使用以下方式启用 ES 模块命名导出

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          esModule: true,
          modules: {
            namedExport: true,
          },
        },
      },
    ],
  },
};

要为 namedExport 设置自定义名称,可以使用 exportLocalsConvention 选项作为一个函数。

请参见下面的 示例 部分。

exportGlobals

类型

type exportsGLobals = boolean;

默认值:false

允许 css-loader 从全局类或 ID 导出名称,以便你可以将其用作本地名称。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            exportGlobals: true,
          },
        },
      },
    ],
  },
};
exportLocalsConvention

类型

type exportLocalsConvention =
  | "as-is"
  | "camel-case"
  | "camel-case-only"
  | "dashes"
  | "dashes-only"
  | ((name: string) => string);

默认值: 取决于 modules.namedExport 选项的值

  • 如果为 true - as-is
  • 否则为 camel-case-only(类名转换为 camelCase,原始名称被移除)。

[!警告]

当命名导出为 false 时,本地名称会转换为 camelCase,即 exportLocalsConvention 选项默认值为 camelCaseOnly。你可以将其设置回任何其他有效选项,但不是有效 JavaScript 标识符的选择器可能会遇到未实现整个模块规范的问题。

导出类名的样式。

string

默认情况下,导出的 JSON 键镜像了类名(即 as-is 值)。

名称类型描述
'as-is'字符串类名将按原样导出。
'camel-case'字符串类名将被 camelCased,但原始类名不会从本地变量中移除。
'camel-case-only'字符串类名将被 camelCased,并且原始类名将从本地变量中移除。
'dashes'字符串类名中只有破折号将被 camelCased
'dashes-only'字符串类名中的破折号将被 camelCased,原始类名将从本地变量中移除

file.css

.class-name {
}

file.js

import { className } from "file.css";

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            exportLocalsConvention: "camel-case-only",
          },
        },
      },
    ],
  },
};
function

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            exportLocalsConvention: function (name) {
              return name.replace(/-/g, "_");
            },
          },
        },
      },
    ],
  },
};

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            exportLocalsConvention: function (name) {
              return [
                name.replace(/-/g, "_"),
                // dashesCamelCase
                name.replace(/-+(\w)/g, (match, firstLetter) =>
                  firstLetter.toUpperCase(),
                ),
              ];
            },
          },
        },
      },
    ],
  },
};
exportOnlyLocals

类型

type exportOnlyLocals = boolean;

默认值:false

仅导出本地变量。

在为预渲染(例如 SSR)使用 css 模块很有用

对于使用 mini-css-extract-plugin 进行预渲染,你应该在预渲染包中使用此选项,而不是 style-loader!css-loader

它不嵌入 CSS;它只导出标识符映射。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            exportOnlyLocals: true,
          },
        },
      },
    ],
  },
};
getJSON

类型

type getJSON = ({
  resourcePath,
  imports,
  exports,
  replacements,
}: {
  resourcePath: string;
  imports: object[];
  exports: object[];
  replacements: object[];
}) => Promise<void> | void;

默认值:undefined

启用回调以输出 CSS 模块映射 JSON。

回调函数将接收一个包含以下内容的对象

  • resourcePath: 原始资源的绝对路径,例如 /foo/bar/baz.module.css

  • imports: 一个导入对象数组,包含导入类型和文件路径的数据,例如:

[
  {
    "type": "icss_import",
    "importName": "___CSS_LOADER_ICSS_IMPORT_0___",
    "url": "\"-!../../../../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[4].use[1]!../../../../../node_modules/postcss-loader/dist/cjs.js!../../../../../node_modules/sass-loader/dist/cjs.js!../../../../baz.module.css\"",
    "icss": true,
    "index": 0
  }
]

(请注意,这将包括所有导入,而不仅仅是与 CSS 模块相关的导入。)

  • exports: 一个导出对象数组,包含导出的名称和值,例如:
[
  {
    "name": "main",
    "value": "D2Oy"
  }
]
  • replacements: 一个导入替换对象数组,用于链接 importsexports,例如:
{
  "replacementName": "___CSS_LOADER_ICSS_IMPORT_0_REPLACEMENT_0___",
  "importName": "___CSS_LOADER_ICSS_IMPORT_0___",
  "localName": "main"
}

使用 getJSON,可以输出一个包含所有 CSS 模块映射的文件。

在以下示例中,我们使用 getJSON 缓存规范映射并为任何组合值(通过 composes)添加占位符,并且我们使用自定义插件合并这些值并将其输出到文件

webpack.config.js

const path = require("path");
const fs = require("fs");

const CSS_LOADER_REPLACEMENT_REGEX =
  /(___CSS_LOADER_ICSS_IMPORT_\d+_REPLACEMENT_\d+___)/g;
const REPLACEMENT_REGEX = /___REPLACEMENT\[(.*?)]\[(.*?)]___/g;
const IDENTIFIER_REGEX = /\[(.*?)]\[(.*?)]/;
const replacementsMap = {};
const canonicalValuesMap = {};
const allExportsJson = {};

function generateIdentifier(resourcePath, localName) {
  return `[${resourcePath}][${localName}]`;
}

function addReplacements(resourcePath, imports, exportsJson, replacements) {
  const importReplacementsMap = {};

  // create a dict to quickly identify imports and get their absolute stand-in strings in the currently loaded file
  // e.g., { '___CSS_LOADER_ICSS_IMPORT_0_REPLACEMENT_0___': '___REPLACEMENT[/foo/bar/baz.css][main]___' }
  importReplacementsMap[resourcePath] = replacements.reduce(
    (acc, { replacementName, importName, localName }) => {
      const replacementImportUrl = imports.find(
        (importData) => importData.importName === importName,
      ).url;
      const relativePathRe = /.*!(.*)"/;
      const [, relativePath] = replacementImportUrl.match(relativePathRe);
      const importPath = path.resolve(path.dirname(resourcePath), relativePath);
      const identifier = generateIdentifier(importPath, localName);
      return { ...acc, [replacementName]: `___REPLACEMENT${identifier}___` };
    },
    {},
  );

  // iterate through the raw exports and add stand-in variables
  // ('___REPLACEMENT[<absolute_path>][<class_name>]___')
  // to be replaced in the plugin below
  for (const [localName, classNames] of Object.entries(exportsJson)) {
    const identifier = generateIdentifier(resourcePath, localName);

    if (CSS_LOADER_REPLACEMENT_REGEX.test(classNames)) {
      // if there are any replacements needed in the concatenated class names,
      // add them all to the replacements map to be replaced altogether later
      replacementsMap[identifier] = classNames.replaceAll(
        CSS_LOADER_REPLACEMENT_REGEX,
        (_, replacementName) =>
          importReplacementsMap[resourcePath][replacementName],
      );
    } else {
      // otherwise, no class names need replacements so we can add them to
      // canonical values map and all exports JSON verbatim
      canonicalValuesMap[identifier] = classNames;

      allExportsJson[resourcePath] = allExportsJson[resourcePath] || {};
      allExportsJson[resourcePath][localName] = classNames;
    }
  }
}

function replaceReplacements(classNames) {
  return classNames.replaceAll(
    REPLACEMENT_REGEX,
    (_, resourcePath, localName) => {
      const identifier = generateIdentifier(resourcePath, localName);

      if (identifier in canonicalValuesMap) {
        return canonicalValuesMap[identifier];
      }

      // Recurse through other stand-in that may be imports
      const canonicalValue = replaceReplacements(replacementsMap[identifier]);

      canonicalValuesMap[identifier] = canonicalValue;

      return canonicalValue;
    },
  );
}

function getJSON({ resourcePath, imports, exports, replacements }) {
  const exportsJson = exports.reduce((acc, { name, value }) => {
    return { ...acc, [name]: value };
  }, {});

  if (replacements.length > 0) {
    // replacements present --> add stand-in values for absolute paths and local names,
    // which will be resolved to their canonical values in the plugin below
    addReplacements(resourcePath, imports, exportsJson, replacements);
  } else {
    // no replacements present --> add to canonicalValuesMap verbatim
    // since all values here are canonical/don't need resolution
    for (const [key, value] of Object.entries(exportsJson)) {
      const id = `[${resourcePath}][${key}]`;

      canonicalValuesMap[id] = value;
    }

    allExportsJson[resourcePath] = exportsJson;
  }
}

class CssModulesJsonPlugin {
  constructor(options) {
    this.options = options;
  }

  // eslint-disable-next-line class-methods-use-this
  apply(compiler) {
    compiler.hooks.emit.tap("CssModulesJsonPlugin", () => {
      for (const [identifier, classNames] of Object.entries(replacementsMap)) {
        const adjustedClassNames = replaceReplacements(classNames);

        replacementsMap[identifier] = adjustedClassNames;

        const [, resourcePath, localName] = identifier.match(IDENTIFIER_REGEX);

        allExportsJson[resourcePath] = allExportsJson[resourcePath] || {};
        allExportsJson[resourcePath][localName] = adjustedClassNames;
      }

      fs.writeFileSync(
        this.options.filepath,
        JSON.stringify(
          // Make path to be relative to `context` (your project root)
          Object.fromEntries(
            Object.entries(allExportsJson).map((key) => {
              key[0] = path
                .relative(compiler.context, key[0])
                .replace(/\\/g, "/");

              return key;
            }),
          ),
          null,
          2,
        ),
        "utf8",
      );
    });
  }
}

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: { modules: { getJSON } },
      },
    ],
  },
  plugins: [
    new CssModulesJsonPlugin({
      filepath: path.resolve(__dirname, "./output.css.json"),
    }),
  ],
};

在上述示例中,所有导入别名在 getJSON 中被替换为 ___REPLACEMENT[<resourcePath>][<localName>]___,并在自定义插件中得到解析。所有 CSS 映射都包含在 allExportsJson

{
  "foo/bar/baz.module.css": {
    "main": "D2Oy",
    "header": "thNN"
  },
  "foot/bear/bath.module.css": {
    "logo": "sqiR",
    "info": "XMyI"
  }
}

这将被保存到名为 output.css.json 的本地文件中。

importLoaders

类型

type importLoaders = number;

默认值:0

允许启用/禁用或设置在 CSS loader 之前应用于 @import 规则、CSS 模块和 ICSS 导入的加载器数量,即 @import/composes/@value value from './values.css'/等。

importLoaders 选项允许你配置在 css-loader 之前应用于 @import 资源和 CSS 模块/ICSS 导入的加载器数量。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              importLoaders: 2,
              // 0 => no loaders (default);
              // 1 => postcss-loader;
              // 2 => postcss-loader, sass-loader
            },
          },
          "postcss-loader",
          "sass-loader",
        ],
      },
    ],
  },
};

未来当模块系统(即 webpack)支持按来源匹配加载器时,这可能会改变。

sourceMap

类型

type sourceMap = boolean;

默认值:取决于 compiler.devtool 的值

默认情况下,Source Map 的生成取决于 devtool 选项。除了 evalfalse 值,所有其他值都启用 Source Map 生成。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          sourceMap: true,
        },
      },
    ],
  },
};

esModule

类型

type esModule = boolean;

默认值:true

默认情况下,css-loader 生成使用 ES 模块语法的 JS 模块。

在某些情况下,使用 ES 模块是有益的,例如模块串联摇树优化

你可以使用以下方式启用 CommonJS 模块语法

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          esModule: false,
        },
      },
    ],
  },
};

exportType

类型

type exportType = "array" | "string" | "css-style-sheet";

默认值: 'array'

允许将样式导出为包含模块的数组、字符串或可构造样式表(即CSSStyleSheet)。

默认值为 'array',即加载器导出一个带有特定 API 的模块数组,该 API 用于 style-loader 或其他。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        assert: { type: "css" },
        loader: "css-loader",
        options: {
          exportType: "css-style-sheet",
        },
      },
    ],
  },
};

src/index.js

import sheet from "./styles.css" assert { type: "css" };

document.adoptedStyleSheets = [sheet];
shadowRoot.adoptedStyleSheets = [sheet];

'array'

默认导出是包含特定 API 的模块数组,该 API 用于 style-loader 或其他。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/i,
        use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"],
      },
    ],
  },
};

src/index.js

// `style-loader` applies styles to DOM
import "./styles.css";

'string'

[!警告]

你不应该将 style-loadermini-css-extract-plugin 与此值一起使用。

如果你想将其与 CSS 模块 一起使用,则应启用 esModule 选项。默认情况下,对于本地变量,将使用命名导出

默认导出是 string 类型。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/i,
        use: ["css-loader", "postcss-loader", "sass-loader"],
      },
    ],
  },
};

src/index.js

import sheet from "./styles.css";

console.log(sheet);

'css-style-sheet'

[!警告]

@import 规则尚未被允许,更多信息

[!警告]

你不再需要 style-loader,请将其移除。

[!警告]

如果你想将其与 CSS 模块 一起使用,则应启用 esModule 选项。默认情况下,对于本地变量,将使用命名导出

[!警告]

由于一个bug,Chrome 目前不支持 Source Map

默认导出是一个可构造样式表(即CSSStyleSheet)。

对于自定义元素和 Shadow DOM 很有用。

更多信息

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        assert: { type: "css" },
        loader: "css-loader",
        options: {
          exportType: "css-style-sheet",
        },
      },

      // For Sass/SCSS:
      //
      // {
      //   assert: { type: "css" },
      //   rules: [
      //     {
      //       loader: "css-loader",
      //       options: {
      //         exportType: "css-style-sheet",
      //         // Other options
      //       },
      //     },
      //     {
      //       loader: "sass-loader",
      //       options: {
      //         // Other options
      //       },
      //     },
      //   ],
      // },
    ],
  },
};

src/index.js

// Example for Sass/SCSS:
// import sheet from "./styles.scss" assert { type: "css" };

// Example for CSS modules:
// import sheet, { myClass } from "./styles.scss" assert { type: "css" };

// Example for CSS:
import sheet from "./styles.css" assert { type: "css" };

document.adoptedStyleSheets = [sheet];
shadowRoot.adoptedStyleSheets = [sheet];

为了迁移目的,你可以使用以下配置

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        oneOf: [
          {
            assert: { type: "css" },
            loader: "css-loader",
            options: {
              exportType: "css-style-sheet",
              // Other options
            },
          },
          {
            use: [
              "style-loader",
              {
                loader: "css-loader",
                options: {
                  // Other options
                },
              },
            ],
          },
        ],
      },
    ],
  },
};

示例

推荐

对于 生产 构建,建议从你的 bundle 中提取 CSS,以便稍后能够并行加载 CSS/JS 资源。

这可以通过使用 mini-css-extract-plugin 实现,因为它会创建单独的 CSS 文件。

对于 开发 模式(包括 webpack-dev-server),你可以使用 style-loader,因为它使用多个 <style></style> 标签将 CSS 注入到 DOM 中,并且工作速度更快。

[!注意]

不要同时使用 style-loadermini-css-extract-plugin

webpack.config.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV !== "production";

module.exports = {
  module: {
    rules: [
      {
        // If you enable `experiments.css` or `experiments.futureDefaults`, please uncomment line below
        // type: "javascript/auto",
        test: /\.(sa|sc|c)ss$/i,
        use: [
          devMode ? "style-loader" : MiniCssExtractPlugin.loader,
          "css-loader",
          "postcss-loader",
          "sass-loader",
        ],
      },
    ],
  },
  plugins: [].concat(devMode ? [] : [new MiniCssExtractPlugin()]),
};

使用 /* webpackIgnore: true */ 注释禁用 URL 解析

借助 /* webpackIgnore: true */ 注释,可以禁用对规则和单个声明的源处理。

/* webpackIgnore: true */
@import url(./basic.css);
@import /* webpackIgnore: true */ url(./imported.css);

.class {
  /* Disabled url handling for the all urls in the 'background' declaration */
  color: red;
  /* webpackIgnore: true */
  background: url("./url/img.png"), url("./url/img.png");
}

.class {
  /* Disabled url handling for the first url in the 'background' declaration */
  color: red;
  background:
    /* webpackIgnore: true */ url("./url/img.png"), url("./url/img.png");
}

.class {
  /* Disabled url handling for the second url in the 'background' declaration */
  color: red;
  background:
    url("./url/img.png"),
    /* webpackIgnore: true */ url("./url/img.png");
}

/* prettier-ignore */
.class {
  /* Disabled url handling for the second url in the 'background' declaration */
  color: red;
  background: url("./url/img.png"),
    /* webpackIgnore: true */
    url("./url/img.png");
}

/* prettier-ignore */
.class {
  /* Disabled url handling for third and sixth urls in the 'background-image' declaration */
  background-image: image-set(
    url(./url/img.png) 2x,
    url(./url/img.png) 3x,
    /* webpackIgnore:  true */ url(./url/img.png) 4x,
    url(./url/img.png) 5x,
    url(./url/img.png) 6x,
    /* webpackIgnore:  true */
    url(./url/img.png) 7x
  );
}

资源

以下 webpack.config.js 可以加载 CSS 文件,将小的 PNG/JPG/GIF/SVG 图像以及字体作为数据 URL 嵌入,并将较大的文件复制到输出目录。

对于 webpack v5

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
        // More information here https://webpack.js.cn/guides/asset-modules/
        type: "asset",
      },
    ],
  },
};

提取

对于生产构建,建议从你的 bundle 中提取 CSS,以便稍后能够并行加载 CSS/JS 资源。

  • 这可以通过在生产模式下运行 mini-css-extract-plugin 来提取 CSS 实现。

  • 作为替代方案,如果寻求更好的开发性能和模拟生产环境的 CSS 输出。extract-css-chunks-webpack-plugin 提供了一个热模块重载友好的 mini-css-extract-plugin 扩展版本。在开发环境中,它支持实时 CSS 文件 HMR,在非开发环境中,它的工作方式与 mini-css 类似。

纯 CSS、CSS 模块和 PostCSS

当你的项目中包含纯 CSS(不带 CSS 模块)、CSS 模块和 PostCSS 时,你可以使用此设置

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        // For pure CSS - /\.css$/i,
        // For Sass/SCSS - /\.((c|sa|sc)ss)$/i,
        // For Less - /\.((c|le)ss)$/i,
        test: /\.((c|sa|sc)ss)$/i,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              // Run `postcss-loader` on each CSS `@import` and CSS modules/ICSS imports, do not forget that `sass-loader` compile non CSS `@import`'s into a single file
              // If you need run `sass-loader` and `postcss-loader` on each CSS `@import` please set it to `2`
              importLoaders: 1,
            },
          },
          {
            loader: "postcss-loader",
            options: { plugins: () => [postcssPresetEnv({ stage: 0 })] },
          },
          // Can be `less-loader`
          {
            loader: "sass-loader",
          },
        ],
      },
      // For webpack v5
      {
        test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
        // More information here https://webpack.js.cn/guides/asset-modules/
        type: "asset",
      },
    ],
  },
};

使用别名解析未解析的 URL

index.css

.class {
  background: url(/assets/unresolved/img.png);
}

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  resolve: {
    alias: {
      "/assets/unresolved/img.png": path.resolve(
        __dirname,
        "assets/real-path-to-img/img.png",
      ),
    },
  },
};

带自定义导出名称的命名导出

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        loader: "css-loader",
        options: {
          modules: {
            namedExport: true,
            exportLocalsConvention: function (name) {
              return name.replace(/-/g, "_");
            },
          },
        },
      },
    ],
  },
};

分离仅限 可互操作 CSSCSS 模块 功能

以下设置是允许仅使用 可互操作 CSS 功能(例如 :import:export),而不使用其他 CSS 模块 功能的示例,通过为所有不匹配 *.module.scss 命名约定的文件设置 mode 选项。这仅供参考,因为在 v4 之前,将 ICSS 功能应用于所有文件是 css-loader 的默认行为。

同时,在此示例中,所有匹配 *.module.scss 的文件都被视为 CSS 模块

假设一个示例场景是,项目需要将 canvas 绘图变量与 CSS 同步 —— canvas 绘图使用与 HTML 背景(通过 CSS 中的类名设置)相同的颜色(通过 JavaScript 中的颜色名称设置)。

webpack.config.js

module.exports = {
  module: {
    rules: [
      // ...
      // --------
      // SCSS ALL EXCEPT MODULES
      {
        test: /\.scss$/i,
        exclude: /\.module\.scss$/i,
        use: [
          {
            loader: "style-loader",
          },
          {
            loader: "css-loader",
            options: {
              importLoaders: 1,
              modules: {
                mode: "icss",
              },
            },
          },
          {
            loader: "sass-loader",
          },
        ],
      },
      // --------
      // SCSS MODULES
      {
        test: /\.module\.scss$/i,
        use: [
          {
            loader: "style-loader",
          },
          {
            loader: "css-loader",
            options: {
              importLoaders: 1,
              modules: {
                mode: "local",
              },
            },
          },
          {
            loader: "sass-loader",
          },
        ],
      },
      // --------
      // ...
    ],
  },
};

variables.scss

文件仅被视为 ICSS

$colorBackground: red;
:export {
  colorBackgroundCanvas: $colorBackground;
}

Component.module.scss

文件被视为 CSS 模块

@import "variables.scss";
.componentClass {
  background-color: $colorBackground;
}

Component.jsx

在 JavaScript 中直接使用 CSS 模块 功能和 SCSS 变量。

import * as svars from "variables.scss";
import * as styles from "Component.module.scss";

// Render DOM with CSS modules class name
// <div className={styles.componentClass}>
//   <canvas ref={mountsCanvas}/>
// </div>

// Somewhere in JavaScript canvas drawing code use the variable directly
// const ctx = mountsCanvas.current.getContext('2d',{alpha: false});
ctx.fillStyle = `${svars.colorBackgroundCanvas}`;

贡献

我们欢迎所有贡献!

如果你是新用户,请在提交问题或拉取请求之前花一些时间阅读我们的贡献指南。

贡献

许可证

MIT