CSS Flat(CSS 扁平化)是一种模块化解决方案,基于Post CSS生态开发。
主要解决问题:
- 达到模块化CSS的能力
- 解决由于业务的持续迭代,导致的CSS样式文件的线性增长问题(CSS Modules尤其明显)
CSS Flat 将CSS样式格式化为单条样式,开发时只需要按照正常文件书写:
.className {
display: block;
color: red;
margin: 0 auto;
}
.className:hover {
color: green;
magin-top: 10px;
}
Flat化之后:
.a-d-b {
display: block;
}
.a-c-1 {
color: red;
}
.a-m-2 {
margin: 0 auto;
}
.a-c_h-3:hover {
color: green;
}
.css-flat .a-mt_h-4:hover {
margin-top: 10px;
}
当你在js文件中 import CSS Flat文件时,会export一个对象,该对象包含Flat化之后 的信息(className: newClassNames):
import styles from "./style.css"; // { classNames: 'a-d-b a-c-1 a-m-2 a-c_h-3 a-mt_h-4 '}
// import { className } from "./style.css";
element.innerHTML = '<div class="' + styles.className + '">';
- 仅处理单层类选择器
/* 不支持 */
.main.home-main {}
.main .title {}
.main span {}
#main {}
- 简写权重小于非简写权重
- 媒体查询权重大于普通样式,不同条件的媒体查询权重需自行配置
npm install --save-dev css-flat-loader
目前依赖在CSS Modules的基础上来判断是否需要Flat话,后续会独立出来,详见demo
{
test: /\.less$/,
// loader: "style-loader!css-flat-loader!css-loader?modules!less-loader",
loader: ExtractTextPlugin.extract("css-flat-loader!css?modules&localIdentName=_[local]_!less")
},
建议添加如下设置来对样式文件进行检测:
declaration-block-no-shorthand-property-overrides: true 查看
max-nesting-depth: [0, ignore: ["blockless-at-rules"]] 查看
selector-max-class: 1 查看
selector-max-id: 0 查看
// 配置文件css-flat.config.js
module.exports = {
plugins: [
require('precss')(),
require('autoprefixer')(),
]
}
注:对于px2rem, autoprefixer等推荐在css-flat.config.js的plugins中配置
flat后的样式公式如下:
.htmlClass*n .prefix-declProp(_(pseudo)(_atRule))-declValue {}
- htmlClass 根节点类名,用来增加权重,如margin-top的权重大于margin,n为'-'的个数
- 当atRule存在,但无伪类时,pseudo为空字符,但下划线(_)保留,避免冲突
- 当提供的map映射无相关属性时,脚本会自动从1自增分配,所以如需自定义提供map,不要提供数字,以免冲突
属性 | 类型 | 默认值 | 描述 |
---|---|---|---|
htmlClass |
{string} |
'css-flat' |
根节点类名,请自行在html标签上添加 |
prefix |
{string} |
'a' |
类名前缀 |
declPropMap |
{Object} |
见属性映射 | 属性映射 |
pseudoMap |
{Object} |
见伪类映射 | 伪类映射 |
atRules |
{Array} |
[] |
@规则的映射,如@media等,数组顺序代表权重 |
declValueMap |
{Object} |
见值映射 | 值映射 |
plugins |
{Array} |
[] |
插件 |
sourceMap |
{Bool} |
false |
sourceMap为true时,会保留css modules hash之后的类,但属性会改变成--sourceMap-xxx , 间接达到sourceMap的功能 |
对于一些大型webview APP可按照规则容器内置通用common.css, 上线时做一次diff,仅需线上加载common.css不包含的CSS, 进一步降低样式文件,提升加载速度。
-
css文件减少,那html或js文件增大了 答:没错,这是肯定的,不过不知你是否注意到,当你打开浏览器控制台查看元素,发现在元素上生效的样式并没有那么多条,更多的是层层覆盖。 针对该问题做了如下优化:
- 扁平化之前会使用cssnano进行合并
- 扁平化之后处理prefixer
- 规则尽可能简短
- 经过扁平化处理后注入到js中的也仅仅是一个对象,标签上也只是个对象值的引用
-
DOM节点的操作 答:经过处理后的类名是不具有可读性的,如果你使用的是react,那么也没必要操作dom,类名的可读性不再那么重要。如果你依然有操作dom的需求,你可以在模板中自行写类名。
-
sourceMap 答:这块的确不是特别好搞,目前的方案是开启sourceMap后,会保留css-modules hash后的类名,并将该类名下的样式自定义化使其不生效(如:--sourceMap-color: red),用于定位当前节点样式源文件的位置,基本可以满足需求。
-
简写和非简写的处理 答:优先使用cssnona进行合并,合并不了的遵循以下规则:非简写权重大于简写 如margin-top 会被处理成 .css-flat .xxx {margin-top: ...} border-top-left-radius 会被处理成 .css-flat.css-flat.css-falt .xxx {margin-top: ...} 所以你需要在html根节点上手动添加css-flat类名(可自定义) 对于媒体查询规则类似,只是权重可自定义,更加灵活
-
不支持嵌套,只支持类 答:对,该方案的核心思想就是扁平化处理css,对于嵌套我们不推荐,也不支持,如果你的项目已经组件化处理,单个组件的模板不会太大,也不应该太大,非嵌套的类方式不管从项目的后期可维护性上,还是浏览器的渲染上,都是利大于弊的。
-
样式合并 如style.button + style.disabled 答:推荐使用less如下方式:
.button{
}
.button-diabled{
.button()
}
因扁平化处理后的样式顺序改变不应该影响最终的渲染结果,所以单个html标签上的类名不应该出现相同属性的样式,直接使用style.button + style.disabled的方式是不安全的。 细心的朋友可能已经注意到我们的规则.prefix-declProp((pseudo)(atRule))-declValue {} 既有-又有 这两种分隔符,这里就是预留给处理相同属性的,你可以自己处理declProp((pseudo)(_atRule))相同的部分。
因开发中要支持样式文件的按需加载及热更新,无法过滤浏览器中已加载的样式,会很多重复的样式,在控制台中看着很不爽。上线时还需自行添加plugin处理重复样式。