css-loader
将 @import
和 url()
解析为 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 函数 url
和 image-set
的处理。
false
,css-loader
将不会解析 url
或 image-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
模式时,可以省略该值。
控制应用于输入样式的编译级别。
local
、global
和 pure
处理 class
和 id
的作用域以及 @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
, pure
和 icss
。
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]
- 字符串的哈希值,根据 localIdentHashSalt
、localIdentHashFunction
、localIdentHashDigest
、localIdentHashDigestLength
、localIdentContext
、resourcePath
和 exportName
生成[<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
允许指定一个函数来生成类名。
默认情况下,我们使用内置函数生成类名。
如果你的自定义函数返回 null
或 undefined
,则使用内置生成器作为 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 模块对本地变量的命名导出。
[!警告]
当
namedExport
为true
时,不能直接使用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
: 一个导入替换对象数组,用于链接 imports
和 exports
,例如:{
"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
选项。除了 eval
和 false
值,所有其他值都启用 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-loader
或mini-css-extract-plugin
与此值一起使用。
默认导出是 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
,请将其移除。
[!警告]
[!警告]
由于一个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-loader
和mini-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 模块)、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",
},
],
},
};
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, "_");
},
},
},
},
],
},
};
可互操作 CSS
和 CSS 模块
功能以下设置是允许仅使用 可互操作 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}`;
我们欢迎所有贡献!
如果你是新用户,请在提交问题或拉取请求之前花一些时间阅读我们的贡献指南。