深入了解CSS模块

253 阅读8分钟

简介

根据官方的CSS模块GitHub仓库,CSS模块是一个CSS文件,其中所有的类名和动画名默认都在本地范围内。相比之下,在一个典型的CSS文件中,所有的CSS选择器都在全局范围内。

在本教程中,我们将研究一些在编写CSS时经常出现的问题,并学习CSS模块如何帮助我们避免这些问题。然后,我们将把CSS模块集成到一个React应用程序中。

让我们开始吧

前提条件

  • 具有HTML和CSS的知识
  • 对React的工作知识

熟悉CSS模块是一个额外的好处。

了解常见的CSS问题

CSS中的所有选择器都是全局变量。随着应用程序的扩展,使用全局变量和管理依赖关系变得越来越困难。当几个开发人员都在做这个应用时,事情就变得更加棘手了。

这就是原因。

名称碰撞

比方说,在设计博客风格时,我们添加了类名post 来表示主页上的文章。另一个开发者创建了侧边栏,也为侧边栏上的帖子添加了类名post 。像这样的错误会导致名称冲突,见这里。

/* styles */
.main .post {
  color: #000000;
  font-size: 2rem;
}

.sidebar .post {
  color: #FFFFFF;
  font-size: 1rem;
}

随着 应用程序的扩展, 你更有可能遇到名称碰撞 (可能 会损害性能)。

清理死代码的困难

当一个元素或React组件从我们的代码中被删除时,我们也需要删除其样式。然而,在大型应用程序中,要确定一个类是否在使用,可能非常困难。CSS并没有提供一个开箱即用的解决方案。

依赖性管理

在使用全局变量时,依赖关系没有明确的定义,因此很难确定哪些样式会被继承或通过组合应用。

在CSS中还有一些隐含的依赖关系,仅仅通过扫描代码是不容易识别的。例如,一个带有position: absolute 的元素是相对于它的父元素position: relative 的。

依赖关系是一个巨大的原因,维护我们的CSS代码的难易程度在很大程度上取决于我们的依赖关系的结构如何。

评估解决方案

BEM - Block Element Modifier是HTML和CSS中流行的类的命名规则,旨在帮助开发者理解两种语言之间的关系。BEM通过提供严格的命名规则解决了上述问题。

在BEM中,一个Block ,是一个独立的元素,它可以独立成章;它通常是一个父元素,如.btn{} 。一个Element 是指一个Block 的子元素;它没有独立的意义,在Block 的名称后面用两个下划线表示(例如,.btn__text )。

修饰符是BlockElement 上的一个标志,用于对其进行样式化。它在BlockElement 的名称后用两个连字符表示(例如,.btn--primary {} )。

// Block Element
.btn {}

// Element that depends on the Block often a child element
.btn__text {
  // rules
}

// Modifiers that changes the styles of the block
.btn--primary {}
.btn--small {}

BEM命名方法的好处是,尽管是全局性的,但所有的选择器都被修改器所覆盖。然而,手动添加BEM命名是重复的、相当乏味的,而且容易出现人为错误。

你最终可能会花费大量的时间来弄清楚某些东西是Block 还是Element 。在我看来,Bulma CSS的创建者Jeremy Thomas完美地概括了这个问题。

Bulma CSS Jeremy Thomas CSS Time Writing Graph

开发涉及到将困难的问题自动化,所以我们应该能够通过合适的工具轻松实现自动化命名。

注意:尽管CSS模块使我们能够对我们的样式进行范围控制,但我们仍然可以通过在类名前加上""来声明全局类。:global:

:global .title {
font-size: 2rem;
}

CSS模块的优势

大多数现代JavaScript和CSS的工作流程都趋向于基于组件的架构,但CSS的进展纯粹是传统的,实际上并不被语言所支持。

前面讨论的BEM就是一个完美的例子。一句被称为软件工程基本定理的熟语宣称:"计算机科学中的每一个问题都可以通过一个额外的抽象层来解决"。

CSS模块是一个薄薄的抽象层,它封装了引入语言的新概念。因此,CSS模块的写法就像普通的CSS一样,如下面的代码片段所示。

/* styles.css */
.title {
  font-size: 2rem;
  font-weight: bold;
  color: red;
}
.text {
  font-size: 1.2rem;
  font-weight: 500;
  color: blue;
}

一个区别是,在CSS模块中,我们所有的标记都写在一个JavaScript文件中,如index.js

import styles from "./styles.css";
document.getElementById("app").innerHTML = `
<h1 class=${styles.title}>Hello Vanilla!</h1>
<div class=${styles.text}>
  We use the same configuration as Parcel to bundle this sandbox, you can find more
  info about Parcel 
</div>
`;

当我们从index.js 文件中导入我们的CSS模块时,CSS模块会导出一个带有从本地名称到全局名称的映射的对象。

{
  title: "_src_styles__title",
  text: "_src_styles__text"
}

我们可以看到,CSS模块动态地生成了独特的类名,使我们整个团队的命名自动化。

CSS模块如何工作

像webpack、Browsify和JSPM这样的现代工具使我们能够明确地定义跨语言的依赖关系。因此,我们可以明确地描述每个文件的依赖关系,无论源文件的类型如何。

在下面的代码片段中,只要加载或捆绑MyComponent ,相应的CSS就会像其他依赖关系一样被加载。

import './my-component-name.css';
const MyComponent = () => {
  // component codes
}
export default MyComponent;

CSS模块包括这种新技术,这是现代加载器的关键能力。然而,在基本层面上,需要一个新的规范来描述这些符号的共享方式。

了解ICSS

尽管CSS模块是像普通的CSS一样编写的,但它实际上编译成了一种叫做ICSSInteroperable CSS的低级可互换格式,它是为加载器实现者而不是最终用户设计的。它是标准CSS的超集,是增强CSS的低级文件格式。

你可以将CSS模块纳入广泛的Automa应用程序中,然而,我们将为一个React应用程序设计样式。

用CSS模块设计React应用程序的样式

Create React App v2(及更高版本)开箱即支持CSS Module。我们所要做的就是使用下面的命名规则。

[name].module.css

让我们通过建立一个简单的React应用来看看它的实际效果吧!首先,让我们启动并运行我们的应用程序。

npx create-react-app button-stack
cd botton-stack
npm start

接下来,我们将为我们的应用程序添加CSS模块支持,只需将App.css 文件重命名为App.module.css 。更新App.js 文件中的导入语句以避免错误。

.shadow {
  box-shadow: rgba(50, 50, 50, 0.2) 0 5px 5px 0;
}
.app {
  display: flex;
  justify-content: space-around;
}
.title {
  margin-top: 25%;
  text-align: center;
}

Index.css 文件更新为Index.module.css ,以及Index.js 文件中的导入语句。接下来,在我们的App.js 文件中,添加以下代码。

import { title, app } from './App.module.css';
import Button from './components/Button';
function App() {
    return (
        <div>
            <h1 className={title}>CSS Module Buttons</h1>
            <article className={app}>
                <Button />
            </article>
        </div>
    );
}
export default App;

尽管这段代码的大部分内容应该是熟悉的,但有几件事情我们需要注意一下。首先,我们正在对titleapp 进行解构。我们需要的样式来自styles 对象,它由CSS模块导出。

现在,我们需要创建Button component 。在src 目录中,创建一个components 文件夹。在该文件夹中,创建一个Button.js 和一个Button.module.css 文件;在Button.module.css 文件中添加以下代码。

.normal-button {
    display: inline-flex;
    line-height: 2;
    text-align: center;
    padding: 1px 60px;
    font-family: "IBM Plex Sans";
    font-size: 1rem;
    font-weight: 500;
    border-radius: 4px;
    cursor: pointer;
    composes: shadow from "../App.module.css"
  }

  .danger {
    composes: normal-button;
    background-color: rgb(255, 8, 8);
    border: 2px solid rgb(255, 8, 8);
    color: white;
  }

  .secondary {
    composes: normal-button;
    background-color: rgb(128, 118, 118);
    border: 2px solid rgb(128, 118, 118);
    color: white;
  }

  .info {
    composes: normal-button;
    background-color: rgb(6, 218, 255);
    border: 2px solid rgb(6, 218, 255);
    color: white;
  }

  .warning {
    composes: normal-button;
    background-color: rgb(248, 202, 49);
    border: 2px solid rgb(248, 202, 49);
    color: #ffffff;
  }

  .success {
    composes: normal-button;
    background-color: rgba(30, 156, 41, 0.966);
    border: 2px solid rgba(30, 156, 41, 0.966);
    color: white;
  }

  .primary {
    composes: normal-button;
    background-color: rgba(33, 124, 243, 0.849);
    border: 2px solid rgba(33, 124, 243, 0.849);
    color: #FFFFFF;
  }

在这个文件中,我们有一个普通的按钮类.normal-button ,它由App.module.css ,组成了shadow class

组合是CSS模块中的一个功能,它使我们能够对选择器进行组合。因此,我们可以通过从另一个类中继承样式来组成一个类,但这些composes 规则必须在其他规则之前。

例如,.danger,.info,.primary,.warning, 和.success 类都通过组合继承了来自.normal-botton 的样式。

我们的App.js 文件现在应该看起来像下面的代码。

import { title, app } from './App.module.css';
import Button from './components/Button';
function App() {
    return (
        <div>
            <h1 className={title}>CSS Module Buttons</h1>
            <article className={app}>
                <Button />
            </article>
        </div>
    );
}
export default App;

我们的应用程序显示应该看起来像这里的图片。

CSS Module Final App

你可以查看本教程的完整代码

总结

毫无疑问,CSS模块为CSS语言提供了多年来最重要的改进之一CSS模块最好的一点是,我们可以写出好的老式CSS,并将其纳入各种应用中。它只是为CSS增加了更多的力量

如果你的React应用没有使用Create React App,或者它使用的版本低于第2版,你仍然可以通过使用 [babel-plugin-react-css-module](https://github.com/gajus/babel-plugin-react-css-modules).

深入了解CSS模块 》一文首次出现在LogRocket博客上。