CSS Modules使用详解

  • 2019 年 12 月 4 日
  • 笔记

CSS Modules

CSS 模块化

不管是用jquery还是react开发,都会遇到的一系列 CSS 的问题:

  1. 全局污染
  2. 命名混乱
  3. 依赖引入复杂
  4. 无法共享变量
  5. 代码冗余

通过 JS 来管理 CSS 就很好解决上述列举的问题。CSS 模块化的解决方案有很多,但主要有两类。

一类是彻底抛弃 CSS,使用 JS 或 JSON 来写样式。Radium, jsxstyle ,react-style 属于这一类。优点是能给 CSS 提供 JS 同样强大的模块化能力;缺点是不能利用成熟的 CSS 预处理器(或后处理器) Sass/Less/PostCSS, :hover 和 :active 伪类处理起来复杂。

另一类是依旧使用 CSS,但使用 JS 来管理样式依赖,代表是CSS Modules。CSS Modules 能最大化地结合现有 CSS 生态和 JS 模块化能力。发布时依旧编译出单独的 JS 和 CSS。它并不依赖于 React,只要你使用 Webpack,可以在 Vue/Angular/jQuery 中使用。

启用 CSS Modules

// webpack.config.js  css?modules&localIdentName=[name]__[local]-[hash:base64:5]

加上 modules 即为启用, localIdentName 是设置生成样式的命名规则,[name]表示标签名,[local]表示类名,[hash:base64:5]是按照给定算法生成的序列码。

/* components/test.css */  .active {      color: red;  }  .disabled {      color: gray;  }    /* components/test.js */  import styles from './test.css';    console.log(styles);    elem.outerHTML = `<h1 class=${styles.active}>CSS Modules</h1>`

生成的 HTML 是

<h1 class="h1--active-abc53"> Processing... </h1>

它将根据 styleName 的值在关联的 style 对象中查找对应的 CSS Modules,并为 ReactElement className 属性值添加相匹配的独一无二的 CSS 类名。

上例中 styles的consolelog 打印的结果是:

Object {    active: 'h1--active-abc53',    disabled: 'h1--disabled-def84',  }

CSS Modules 对 CSS 中的 class 名都做了处理,使用对象来保存原 class 和定制处理后的 class 的对应关系。经过这样类名定制处理后,class 名基本就是唯一的,大大降低了项目中样式覆盖的几率。同时可以生成更短的 class 名,减少代码量。

CSS Modules 使用

局部变量和全局变量

  • :local: 做 localIdentName 规则处理
  • :global: 样式编译后不变

如果书写时不加,默认处理为:local

.normal {    color: green;  }    :local(.normal) {    color: green;  }    /* 上面两个等价,默认给每个 class 名外加加了一个 `:local` */      /* 全局样式 */  :global {    .link {      color: green;    }    .box {      color: yellow;    }  }

唯一哈希类名

开启CSS Modules时定义的规则localIdentName=[name]__[local]-[hash:base64:5]会控制对class名的处理, [hash:base64:5]定义的hash计算能保证类名的唯一性。

Compose 组合Class

很多时候我们都需要样式复用,在 CSS Modules 中,一个选择器可以继承另一个选择器的规则,这称为composes 组合。

/* components/test.css */  .bg {    background-color: blue;  }    .title {    composes: bg;    color: white;  }    /* components/test.js */  import styles from './test.css';    elem.outerHTML = `<h1 class=${styles.title}>CSS Modules</h1>`

生成的 HTML 为

<h1 class="h1--bg-fec53 h1--title-Ijf8"> Processing... </h1>

CSS和JS变量共享

:export 关键字可以把 CSS 中的 变量输出到 JS 中:

/* index.scss */  $primary-color: #f40;    :export {    primaryColor: $primary-color;  }    /* app.js */  import style from 'index.scss';    // 会输出 #F40  console.log(style.primaryColor);

CSS Modules使用特点

  1. 不使用选择器,只使用 class 名来定义样式
  2. 不层叠多个 class,只使用一个 class 把所有样式定义好
  3. 不嵌套class
  4. 所有样式通过 composes 组合来实现复用

旧项目的兼容

  1. 如果对一个元素使用多个 class,样式照样生效。
  2. 如何在一个 style 文件中使用同名 class ,编译后仍是同名的。
  3. 如果在 style 文件中使用了 id 选择器,伪类,标签选择器,所有这些选择器将不被转换,原封不动的出现在编译后的 css 中。即 CSS Modules 只会转换 class 名相关样式。
  4. 当类名经过编译生成新的随机类名后,可以解决命名冲突,但因为无法预知最终 class 名,不能通过一般选择器覆盖。现在一般给出的项目中的实践是可以给组件关键节点加上 data-role 属性,然后通过属性选择器来覆盖样式。
  5. 前端项目不可避免会引入 normalize.css 或其它一类全局 css 文件。使用 Webpack 可以让全局样式和 CSS Modules 的局部样式和谐共存。
module: {    loaders: [{      test: /.jsx?$/,      loader: 'babel'    }, {      test: /.scss$/,      exclude: path.resolve(__dirname, 'src/styles'),      loader: 'style!css?modules&localIdentName=[name]__[local]!sass?sourceMap=true'    }, {      test: /.scss$/,      include: path.resolve(__dirname, 'src/styles'),      loader: 'style!css!sass?sourceMap=true'    }]  }