CSS Modules入门Ⅰ:它是什么?为什么要使用它?

CSS Modules入门Ⅰ:它是什么?为什么要使用它?

原文链接:What are CSS Modules and why do we need them?

作者:ROBIN RENDLE

最近我对CSS模块特别感兴趣,要是你以前还从未听说过CSS Modules,那么这个系列就是专门写给你的。首先我们会介绍一下CSS Modules是什么以及为什么要将CSS模块化,之后大概介绍一下如何应用CSS Modules。最后如果你想进一步提升自己,可以阅读第三部分介绍的如何在React环境下使用CSS Modules。

什么是CSS Modules?

根据CSS Modules在Gihub上的项目,它被解释为:

所有的类名和动画名称默认都有各自的作用域的CSS文件。

所以CSS Modules既不是官方标准,也不是浏览器的特性,而是在构建步骤(例如使用Webpack或Browserify)中对CSS类名和选择器限定作用域的一种方式(类似于命名空间)。

我们还是先看一个具体的例子来解释清楚它到底是什么,以及为什么要使用CSS Modules吧。我们通常给HTML加一个CSS的类名来控制它的样式:

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

CSS样式像下面这样:

.title {
  background-color: red;
}

只要把CSS文件加载到HTML文件中,这里的<h1>标签背景就会被设置成红色。我们不需要对HTML或CSS做什么特殊的处理,浏览器本来就支持这种最基本的文件类型。

而CSS Modules的使用方式就不一样了,我们需要把所有的标签写到JS文件里。下面是一个简单的示例:

import styles from "./styles.css";

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

在JS中你可以通过类似styles.title的方式访问CSS文件中的.title类。然后在构建过程中,我们的构建工具会搜索我们用import语句载入的名为styles.css的文件,之后把源文件解析成新的HTML和CSS文件,类名会被特定的格式替换:

HTML

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

CSS

._styles__title_309571057 {
  background-color: red;
}

类属型的.title完全被新生成的命名替换掉了,CSS的源文件也不会被载入。

Hugo Giraudel 的教程 里也提到:

在使用CSS模块时,类名是动态生成的,唯一的,并准确对应到源文件中的各个类的样式。

这也是实现样式作用域的原理。它们被限定在特定的模板里。例如我们在buttons.js里引入buttons.css文件,并使用.btn的样式,在其他诸如forms.js里是不会被.btn影响的,除非它也引入了buttons.css.

可我们是出于什么目的把CSS和HTML文件搞得这么零碎呢?我们为什么要使用CSS模块呢?

为什么要使用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么?担心样式是独立或者依赖别的什么东西么?或者可能会覆盖别的地方的样式?

诸如此类的问题都足够让人头疼了。而且随着项目的扩张,会越来越令人绝望。

但使用CSS Modules就可以避免这些问题。除非你在某个模块中载入一个CSS样式,其他情况下这个样式都不会影响别的HTML.

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的功能。

我们甚至可以通过composes继承其他文件中的样式:

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

BEM代码规范不再是必须的

在编写CSS模块时我们不再需要遵守BEM规范,有这么两个原因:

  1. 简化语法——在CSS Modules中通过类似type.display的表达已经能够很好地被理解了。比起某些BEM中例如.font-size__serif--large这样的命名要更清晰。
  2. 作用域——比如我们的一个CSS中的.big是用来描述文字大小的,另一个CSS中的.big是用来增加间距的。通过使用CSS Modules,我们在能够随意使用相同命名类的同时,更不用担心它们之间起冲突。即使是在同一个文件中引入这两个CSS,最后生成的结果也会把两个样式区别开来。

这仅仅是使用CSS Modules的一点点好处而已。

如果你想了解更多CSS Modules的优点,可以看Glen Madden写的这篇文章

在下一篇中,我们将会讨论如何在项目中通过Webpack来使用CSS Modules,通过使用一些ES6的最新特性以及具体的例子来学习。

编辑于 2016-11-12

文章被以下专栏收录