什么是 CSS Modules 以及我们为什么需要它们?

Avatar of Robin Rendle
Robin Rendle

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

我最近对 CSS Modules 感兴趣。 如果你还没有听说过它们,这篇文章就是为你准备的。 我们将研究这个项目及其目标和目的。 如果你感兴趣,请继续关注,因为下一篇文章将介绍如何开始使用这个理念。 如果你想实现或提升你的使用水平,第 3 部分将介绍如何在 React 环境中使用它们。

文章系列

  1. 什么是 CSS Modules 以及我们为什么需要它们? (您当前所在位置!)
  2. CSS Modules 入门
  3. React + CSS Modules = 😍

什么是 CSS Modules?

根据 仓库,CSS Modules 是

默认情况下所有类名和动画名称都在本地限定范围的 CSS 文件。

因此,CSS Modules 不是一个官方规范浏览器中的实现,而是在构建步骤中(借助 WebpackBrowserify)更改类名和选择器以限定范围(即有点像命名空间)的过程。

这看起来像什么,为什么要这样做? 我们稍后会详细介绍。 首先,请记住 HTML 和 CSS 通常是如何工作的。 类在 HTML 中应用

<h1 class="title">An example heading</h1>

并在 CSS 中设置该类的样式

.title {
  background-color: red;
}

只要该 CSS 应用于 HTML 文档,则该<h1>的背景将为红色。 我们不需要处理 CSS 或 HTML。 浏览器理解这两种文件格式。

CSS Modules 采用了不同的方法。 我们需要在 JavaScript 文件(如index.js)中编写所有标记,而不是编写纯 HTML。 以下是如何工作的示例(我们稍后将查看更真实的示例)

import styles from "./styles.css";

element.innerHTML = 
  `<h1 class="${styles.title}">
     An example heading
   </h1>`;

在我们的构建步骤中,编译器将搜索我们导入的styles.css文件,然后查看我们编写的 JavaScript 并通过styles.title使.title类可访问。 然后,我们的构建步骤将这两者处理成新的、单独的 HTML 和 CSS 文件,并使用新的字符字符串替换 HTML 类和 CSS 选择器类。

我们生成的 HTML 可能如下所示

<h1 class="_styles__title_309571057">
  An example heading
</h1>

我们生成的 CSS 可能如下所示

._styles__title_309571057 {
  background-color: red;
}

类属性和选择器.title完全消失了,被这个全新的字符串替换了;我们的原始 CSS 根本没有提供给浏览器。

正如 Kitty Giraudel 在他的教程中所说

【类】是动态生成的、唯一的,并映射到正确的样式。

这就是样式限定范围的含义。 它们限定在特定的模板中。 如果我们有一个buttons.css文件,我们只会将其导入到buttons.js模板中,并且其中的.btn类将无法访问其他模板(例如forms.js),除非我们也在那里专门导入它。

为什么我们要修改 CSS 和 HTML 来做到这一点? 我们为什么要以这种方式工作?

我们为什么要使用 CSS Modules?

使用 CSS Modules,可以保证单个组件的所有样式

  1. 位于一个地方
  2. 仅应用于该组件,而不是其他任何组件

此外,任何组件都可以拥有真正的依赖项,例如

import buttons from "./buttons.css";
import padding from "./padding.css";

element.innerHTML = `<div class="${buttons.red} ${padding.large}">`;

这种方法旨在解决 CSS 中的全局作用域问题。

您是否曾因为时间或资源不足而只想尽快编写 CSS,而不考虑可能影响的其他内容?

您是否曾经在样式表的底部粘贴一些随机的片段和垃圾,打算以后整理,但从未整理?

您是否曾经遇到过不确定其作用或是否正在使用的样式?

您是否曾经想过是否可以在不破坏任何内容的情况下删除某些样式?想知道样式是否独立存在或依赖于其他内容?或者覆盖了其他地方的样式?

这些问题会导致严重的麻烦、项目截止日期延长以及悲伤、渴望地望着窗外。

使用 CSS Modules 和默认本地作用域的概念,可以避免此问题。 您在编写样式时始终被迫考虑后果。

例如,如果您在 HTML 中使用random-gross-class而不将其应用为 CSS modules 样式类,则样式将不会应用,因为 CSS 选择器将转换为._style_random-gross-class_0038089

composes 关键字

假设我们有一个名为type.css的模块用于我们的文本样式。 在该文件中,我们可能拥有以下内容

.serif-font {
  font-family: Georgia, serif;
}

.display {
  composes: serif-font;
  font-size: 30px;
  line-height: 35px;
}

我们将如下所示在我们的模板中声明其中一个类

import type from "./type.css";

element.innerHTML = 
  `<h1 class="${type.display}">
    This is a heading
  </h1>`;

这将生成如下标记

<h1 class="_type__display_0980340 _type__serif_404840">
  Heading title
</h1>

通过使用composes关键字,这两个类都已绑定到元素,从而避免了类似解决方案(如 Sass 的@extend)的一些问题

我们甚至可以从单独的 CSS 文件中的特定类进行组合

.element {
  composes: dark-red from "./colors.css";
  font-size: 30px;
  line-height: 1.2;
}

无需 BEM

在创建 CSS 模块时,我们不需要使用 BEM。 这是因为两个原因

  1. 易于解析 – 像type.display这样的代码对于开发人员来说与 BEM 风格的.font-size__serif--large一样易读。 当 BEM 选择器变长时,可能更容易在心理上进行解析。
  2. 本地作用域 – 假设我们在一个模块中有一个像.big这样的类,它更改了font-size。 在另一个模块中,我们使用完全相同的类.big,它以不同的数量增加paddingfont-size。 这根本没关系! 它们不会发生冲突,因为样式非常谨慎地限定了范围。 即使一个模块导入两个样式表,它也有一个自定义名称,我们的构建过程专门为此类创建该名称。 换句话说,CSS Modules 消除了特异性问题。

很酷,对吧?

这些只是编写 CSS Modules 的一些好处。

如果您有兴趣了解更多信息,Glen Madden 已经广泛地撰写了关于这种方法的其他一些好处。

本系列的下一篇文章将介绍如何使用 Webpack 和 CSS Modules 启动项目。 我们将使用最新的 ES2015 功能来执行此操作,并查看一些示例代码以指导我们完成整个过程。

进一步阅读

文章系列

  1. 什么是 CSS Modules 以及我们为什么需要它们? (您当前所在位置!)
  2. CSS Modules 入门
  3. React + CSS Modules = 😍