Ikeq ChengIkeq Cheng

The whole problem with the world is that fools and fanatics are always so certain of themselves, but wiser people so full of doubts.

Dec 22, 2018632 words in 3 min


改用 BEM + CSS Module 来组织 Inside 主题的样式

Angular 提供的 CSS 作用域机制很棒,但无疑会增加不少代码体积,特别是有使用 SSR(Server-Side Render) 的情况,因此,本文介绍尝试改用 CSS Module 来重新组织样式的方法。

技术点

1. BEM

BEM 是一套约束 CSS 书写的规范,该规范将类选择器划分成了 Block(B)、Element(E)、Modifier(M)。

  • Block 用于描述可重用的标签快。
  • Element 用于描述 Block 下的无法重用的标签块。
  • Modifier 用于修饰 Block 或 Element 的状态。

2. CSS Module

使用 BEM 规范的类名通常比较长,为了进一步缩小打包代码后的体积,使用 CSS Module 工具将类名 hash:base64 处理。

约定

BEM 书写

Block 与 Element 使用 __链接,Element 与 Modifier 使用 --链接。

借助 webpack,同时为了避免书写上的麻烦,组建样式中除了组建根元素(:host),其他类名不需要加 Block。例如:

1
2
3
4
5
6
7
8
9
10
// app/app.component.scss
.app {
display: block;
}
.header {
display: block;
}
.header--fixed {
position: fixed;
}

最终生成的 CSS 代码将类似:

1
2
3
4
5
6
7
8
9
.app {
display: block;
}
.app__header {
display: block;
}
.app__header--fixed {
position: fixed;
}

对于非组建样式(如全局样式),将统一自动拼接 common 作为 Block 名。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// style/post.scss
.post-table {
display: block;
}
.post-picture {
display: block;
}

// style/icon.scss
.icon {
font-family: Icon;
}
.icon-home:before {
content: '';
}

最终生成的 CSS 代码将类似:

1
2
3
4
5
6
7
8
9
10
11
12
.common__post-table {
display: block;
}
.common__post-picture {
display: block;
}
.common__icon {
font-family: Icon;
}
.common__icon-home:before {
content: '';
}

因此这类样式有冲突的风险。

组建与通用样式之间仅以目录结构作为区分。组建位于 src/app/components 下,通用样式位于 src/app/styles 下。

占位符

占位符用来替换模板及脚本文件中原先的类名。为便于实现,模板代码使用注释的副本。

  • 模板: <!--~ code ~-->
  • 组建选择器: %{tag}
  • 组建样式类名: .{class}
  • 通用样式类名: !.{class}
  • 组建样式类名在脚本中的引用: /*.*/

类名

BEM style class -> md5 -> base64:6

组建选择器

is-[component name -> md5:6]

打包流程

  1. 使用 webpack 处理全部样式文件,生成 css 及 json 格式的类名 map 文件;
  2. 使用 css map 文件预处理组建模板及样式,输出到临时目录;
  3. angular.json 中定义 fileReplacements(一次性定义),替换掉默认的组建模板及脚本文件;
  4. ng build
  5. 合并第 2 步生成的 css 文件到 dist。

Buy me a cup of milk 🥛.