Skip to content

CSS Module #30

@wangjing013

Description

@wangjing013

CSS Module

什么是 CSS Modules

"A CSS Module is a CSS file in which all class names and animation names are scoped locally by default."

CSS模块是一个CSS文件,默认情况下,所有类名和动画名都在局部作用域内.

所以, CSS Modules 不是一个官网的规范或浏览器中的实现,反而是在构建(webpack或Browserify) 过程中修改类名(class name)和选择器(selectors)的作用域.(i.e 类似命名空间).

CSS是全局-定义的类可以在任何地方使用. 但是CSS Modules作用范围局限于使用它的组件中.

为什么使用 CSS Modules

使用 CSS Modules 可以使样式只应用在单个组件中:

  • 不会相互受影响
  • 不会应用在全局作用域
  • 显示的声明依赖

另外, 任何组件都可以有一个真正的依赖关系, 比如:

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

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

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

CSS Modules

Local Scope

默认情况下CSS样式规则是应用在全局的, CSS Modules 通过生成唯一的类名从而间接到达作用域的概念.

// index.css
.title{
  background-color: red;
}
// index.js
import styles from "./styles.css";
const element = document.body;
element.innerHTML =
  `<h1 class="${styles.title}">
     An example heading
   </h1>`;

CSS Modules 并是标准规范, 需要构建工具帮忙完成, 在构建过程中会做如下两件事:

  • 替换 HTML中的 class 属性
  • 替换 CSS选择器

构建后, 我们生成HTML内容如下:

._1K28VpaI9vpLB0C1hETo7e{
  background-color: red;
}
<h1 class="_1K28VpaI9vpLB0C1hETo7e"> An example heading </h1>

Global Scope

通过 :global(.className) 方式显示声明为全局样式. CSS Modules 将忽略这个选择器.

添加全局 class 到 index.css

.title{
  background-color: red;
}
:global(.title){
  background-color: green;
}

在 index.js 中使用 global CSS 类:

import styles from "./styles.css";
const element = document.body;
element.innerHTML =
  `<h1 class="title">
     An example heading
   </h1>`;

编译后内容为:

._1K28VpaI9vpLB0C1hETo7e{
  background-color: red;
}

.title{
  background-color: green;
}

Customized Hash Class Name

css-loader默认hash算法是 [hash:base64]. 通过在 webpack.config.js 中进行自定义:

module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader?',
            options: {
              importLoaders: 1,
              modules: {
                // 自定义局部标识符名称
                localIdentName: "[path][name]__[local]--[hash:base64:5]"
              }
            }
          }
        ]
      }
    ]
  },
  ...
}

Composition

CSS Modules 中选择器之间可以相互继承, 通过 composes 属性

/** index.css **/
.bg {
  background-color: red;
}
.title {
  composes: bg;
}
/** index.js **/
import styles from './index.css';

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

编译后内容如下:

.src-index__bg--2ixbE {
  background-color: red;
}
.src-index__title--1K28V {
}

并没有修改 title, 而是将 title 映射到多个样式:

{
  title: "src-index__title--1K28V src-index__bg--2ixbE"
}

import Other Module

可以继承其它CSS文件中的样式:

/** other.css **/
.bg {
  background-color: red;
}
/** index.css **/
.title {
  composes: bg from './other.css'
}

Exporting values variables

可以在 CSS Modules 中使用变量

/** colors.css **/
@value v-blue: #0c77f8;
@value v-red: #ff0000;
@value v-green: #aaf200;
/** index.css **/
@value colors:  "./colors.css";
@value v-blue, v-red, v-green from colors;
.title {
  background-color: v-blue;
}

案例

demo

BEM VS CSS Modules

当使用CSS Modules 时, 并不需要使用 BEM, 主要是如下两个原因:

  • 容易理解: 像type.display这样的代码对于开发人员来说就像.font-size__serif--large 一样清晰, 当BEM选择器变长时,很容易形成心理分析.

  • 局部作用域: BEM 通过特定约定来达到作用域的概念, 但是不能真正解决样式全局问题. 但是 CSS Modules 可以很好规避这个问题.

推荐

  • 通常如果编写是组件库时可能更推荐使用BEM, 这样可以很方便外部去对其进行样式覆写
  • 其它情况话推荐使用 CSS Module

具体还是根据特定情况来定.

参考

CSS Modules

Understanding the CSS Modules Methodology

总结

  • CSS Modules 可以理解为是一种作用域(类命名空间), 限定类名作用在单个组件中.
  • CSS Modules 并不是一种标准及不能被浏览器直接解析, 需要通过构建工具.
  • CSS Modules 工作原理
    • [classes] 是动态生成的、唯一的, 并该值能映射到正确的样式
    • 通过动态生成值替换掉选择器类名及class属性的值.
  • BEM VS CSS Modules
    通常根据特定场景来定,如果编写通用组件库时推荐使用BEM来达到作用域的概念.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions