最近因为处理 less 自动换主题和 less 的 css-module 的原因,使用了 post-css 这个库,被其强大的功能所吸引,所以写了这个小文章。
首先我们初始化一个项目,我们可以直接使用我已经写好的脚手架 post-css-demo。 这个脚手架中实现了两个语法,一个是模仿 flutter image 容器的 BoxFit,实现的一个属性简写,支持了 fill ,contain ,cover, fitHeight, fitWidth。
写法是这样的 :
.bg-fill {
background-color: #fff;
/* fit: fill fitHeight cover contain */
fit: url(https://flutter.io/assets/flutter-lockup-4cb0ee072ab312e59784d9fbf4fb7ad42688a7fdaea1270ccf6bbf4f34b7e03f.svg)
fill;
}
并且实现了一个temp的属性,我约定 temp-开头的就是一个模板css,类似:
.temp-base {
width: 100%;
height: 100%;
}
然后用法是这样的,prop 代表要用模板,base代码模板的名字。
.bg-fill {
background-color: #fff;
temp: base;
}
所以以下代码
.temp-base {
width: 100%;
height: 100%;
}
.bg-contain {
background-color: #fff;
/* fit: fill fitHeight cover contain */
fit: url(https://flutter.io/assets/flutter-lockup-4cb0ee072ab312e59784d9fbf4fb7ad42688a7fdaea1270ccf6bbf4f34b7e03f.svg)
contain;
temp: base;
}
编译成一个 浏览器是别的css,就是这样:
.bg-contain {
background-color: #fff;
background-size: contain;
background-image: url(https://flutter.io/assets/flutter-lockup-4cb0ee072ab312e59784d9fbf4fb7ad42688a7fdaea1270ccf6bbf4f34b7e03f.svg);
width: 100%;
height: 100%;
}
主要利用了post-css的 walkRules 和 walkDecls 方法。walkRules 会遍历所有的 selector ,也就是一个 {} 包裹的部分,temp就是根据这个原理实现的。 获得 selector,如果selector中包含 temp-
就把他的子节点存起来,等碰到 temp属性,去除并且将其替换。
const tempMap = {};
// 保存模板
if (rule.selector && rule.selector.indexOf("temp-") > -1) {
const tempName = rule.selector.split("-").pop();
tempMap[tempName] = [...rule.nodes];
rule.remove();
return;
}
// 如果是模板属性
if (decl.prop === "temp") {
const value = decl.value;
const decls = tempMap[value] as Declaration[];
const newDecls = decls.map(decl => decl.clone());
if (decls) {
decl.replaceWith(newDecls);
}
}