IkeqIkeq

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 🥛.