缘起
楼主最近搭建了一套前后端开发环境用于开发自己的网站并尝试一些有趣的前端技术,在整个环境运行正常之后,楼主某天心血来潮想将之前的less文件用pcss代替,使用bem的方式去编写css,然后用postcss-bem插件去处理样式规则的转换。在一切配置准备就绪后,正当我兴致勃勃的运行webpack编译打包时,令人惊奇的事情发生了
源代码:
postcss处理后结果:
很明显的是在component声明中的样式规则可以正常输出,但是在子声明中的规则就被干掉了,尽管class可以正常解析。 这强烈的刺激到了楼主的好奇之心,于是我便开始扒这个插件的源码,终于发现了问题所在
postcss-bem插件在处理modifier和descendent类型声明的过程中将当前规则的子节点移植到newRule时用的是moveTo方法,由于我之前升级了webpack4,并为了适配webpack4将其他的loader等也升级到了最新版,而在最新版的postcss中,由css源字符串解析成的抽象语法树已经废弃了该方法,导致当前规则的子节点不能移植到新建的规则newRule中,从而使子声明中的样式规则丢失。 明白了这一点之后我准备给该项目的开发者提issue,却发现npm网站中该项目链接到的Github地址404了,对,你没看错,就是404了
此刻的内心:开发者你出来,我保证不打死你
拓展
基于上述原因,我决定自己开发一个postcss-bem插件,可以帮助到那些和我遇到相同问题的开发者,顺便也帮助大家了解怎样去开发一个postcss插件。
@mozheng-neal/postcss-bem
因为旧项目的代码无人维护而且有可能会有其他坑,所以我选择了重新造轮子,这就是@mozheng-neal/postcss-bem,与此同时通过缓存父级元素选择器的方式,它在元素选择器的生成上拥有更快的速度。大家可以在github上看到该项目的源代码和使用方式(您的star是对我最大的鼓励)。
如何写一个postcss插件
假设我们要实现这样一个功能:在css的每个样式声明中添加一个color属性,值为#666,如果当前声明中已经存在color属性但值不为#666,那么就将其设置为#666。
首先我们来看下一个postcss插件的基本结构
const postcss = require('postcss')
postcss.plugin(pluginName, function (opts) {
return function (root, result) {
// root is the root node object
// this is where your plugin process logic should be placed
}
})
复制代码
postcss会将获取到的css源码字符串转换为js表示的抽象语法树,上述的root就表示该语法树的根节点。要实现我们之前想要的功能,在插件逻辑中我们需要借助一些postcss为节点对象提供的API。
return function (root, result) {
root.walkRules(function (rule) {
// rule为样式文件中的声明块
let hasColorProp = false
rule.walkDecls(function (decl) {
/*
* decl表示声明块中的每条css属性节点
* 比如color:#fff会被解析成decl.prop === color,decl.value === #fff
*/
if (decl.prop === 'color') {
hasColorProp = true
if (decl.value !== '#666') {
decl.value = '#666'
}
}
})
if (!hasColorProp) {
rule.append({prop: 'color', value: '#666'})
}
})
}
复制代码
通过如此简单的一个插件就能完成以往我们需要付出很多手力劳动才能做到的事是不是开心到爆炸
结语
postcss是一个非常便利的css开发工具,使用得当也可以极大提升你的开发效率,而且postcss插件的编写也并不复杂,它绝对是你在前端开发的学习过程中值得去掌握的一个技能。最后再啰嗦一句,欢迎大家使用我的postcss-bem插件,有任何问题欢迎提issue, 我会一直维护这个项目的。
插播一条广告
蘑菇街前端开发团队持续招聘高级/资深前端开发工程师,欢迎各位大佬砸简历给我哦,邮箱m13710224694@163.com