当前位置: 首页 > 科技观察

postCSS剖析——迈向前端架构师的一小步

时间:2023-03-12 03:54:18 科技观察

最近很火的TailwindCSS有一个功能:可以从编译后的css文件中去掉不用的css选择器。此功能由PurgeCSS实现。连接TailwindCSS和PurgeCSS的是一个postCSS插件@fullhuman/postcss-purgecss。不仅是TailwindCSS,很多知名项目都使用了postCSS插件。例如:许多人在他们的项目中使用autoprefixer插件来为css选择器添加不同的“浏览器前缀”。在内部,浏览器版本将根据browserslist[1]指定。然后去caniuse[2]找浏览器版本兼容性支持。最后,能够通过postCSS重写不受支持的css属性。可见postCSS越来越成为前端项目不可或缺的依赖。同时,对于postCSS也有很多误解,比如认为它和Less、Sass是一样的“css预处理器”。本文将从下到上介绍postCSS。希望这篇文章能让大家更深入地了解这个大杀器。什么是postCSSpostCSS是一个css编译器。类比Babel家族的@babel/parser,可以将js代码解析成AST(抽象语法树),利用很多插件(@babel/plugin-xx)的能力重写AST,最后输出重写后的js代码。postCSS使用自己的parser将css代码解析成AST,然后使用很多插件(上面介绍的autoprefixer就是其中之一)对AST进行重写,最后输出重写后的css代码。从这点可以看出,它不同于Less这样的“css预处理器”——postCSS的输入输出产物是css文件。因此,postCSS也被称为“后处理器”,因为它通常处于css处理链的末端。你可以在astexplorer[3]中选择postCSS的AST:language:cssparser:postCSS来理解postCSS是如何解析css的。例如,对于以下css代码:/***IamKaSong*/@mediascreenand(min-width:900px){article{padding:1rem3rem;}}ul{margin:3rem;}ulli{padding:5px;}将是由postCSSAST解析,树结构如下:节点有以下几种类型:Root:根节点,代表一个css文件AtRule:以@开头的声明,比如@charset"UTF-8"or@media(screen){}Rule:Internal包含定义的选择器,如input,button{}声明:key-value键值对,如color:black;评论:单独的评论。选择器、at-rule参数和值注释在节点的节点属性中实现了一个简单的插件。接下来,我们从一个插件的实现来了解开发者是如何介入postCSS编译过程的。postcss-focus[4]将:focus添加到所有:hover选择器以提高键盘可用性。对于下面的代码:.a:hover,.b:hover,.c:hover{opacity:.5;}经过本插件处理后会输出:.a:hover,.b:hover,.c:hover,.a:focus,.b:focus,.c:focus{opacity:.5;}你可以安装postcss和postcss-focus并通过以下演示在控制台上查看结果:constpostcssFocus=require('postcss-focus');constpostcss=require('postcss');constfs=require('fs');//输入的css文件地址constfrom='src/a.css';constto='output/a.css';fs.readFile(from,(err,css)=>{postcss(postcssFocus).process(css,{from,to}).then(result=>{console.log(result.css)})})接下来我们分析postcss-focus源码[5]的实现逻辑:postCSS将输入的css解析成AST,遍历AST中所有Rule类型的节点维护一个数组,遍历该节点的所有selector,每次向数组中压入一个:focus遍历一个包含:hover的选择器选择器会将2中得到的数组concat到节点已有的选择器上,然后输出新的csscore根据修改后的AST的源代码如下:{postcssPlugin:'postcss-focus',//Step1Rule:rule=>{//Step2if(rule.selector.includes(':hover')){letfocuses=[]for(letselectorofrule.selectors){if(selector.includes(':hover')){letreplaced=selector.replace(/:hover/g,':focus')if(!hasAlready(rule.parent,replaced)){focuss.push(replaced)}}}//步骤3if(focuses.length){rule.selectors=rule.selectors.concat(focuses)}}}}本插件只是为了演示插件的基本工作方式。其实插件实现比较粗糙。PostCSS提供了详细的插件创建文档[6]。它甚至为create-postcss-plugin[7]提供模板代码来创建插件。更多可能性得益于表达和重写cssAST的能力,postCSS插件可以实现很多功能。例如:上面的postcss-functions介绍,Declaration节点表示的是“css属性”的键值对,其中的值为“string”类型。然后就可以完全自定义值的解析规则了。body{color:getColor();}通过定义getColor函数,在AST中解析成函数执行,可以在css文件中用js编写逻辑代码。这是postcss-functions[8]stylelint配置不同的lint规则来实现css的静态语法检测。这是stylelint[9]总结了目前的postCSS插件按功能分为以下几类:解决全局CSS问题,比如提供CSSmodules[10]来支持使用不完全兼容的CSS特性,比如autoprefixer[11]格式化,提高CSS可读性图片和文本处理linters,比如stylelintCSS支持不同的语法,比如postcss-html[12]可以解析类HTML文件读到这里,相信你会同意:比起Less和Sass、postCSS是CSS处理领域的大杀器。参考文献[1]browserslist:https://github.com/browserslist/browserslist[2]caniuse:https://caniuse.com/#search=[3]astexplorer:https://astexplorer.net/[4]postcss-focus:https://www.npmjs.com/package/postcss-focus[5]postcss-focus源码:https://github.com/postcss/postcss-focus/blob/master/index.js[6]插件创建文档:https://github.com/postcss/postcss/blob/main/docs/writing-a-plugin.md[7]create-postcss-plugin:https://github.com/csstools/create-postcss-plugin[8]postcss-functions:https://www.npmjs.com/package/postcss-functions[9]stylelint:https://github.com/stylelint/stylelint[10]css模块:https://github.com/madyankin/postcss-modules[11]autoprefixer:https://github.com/postcss/autoprefixer[12]postcss-html:标签中的css语法读到这里,相信你会认同:CompareLess、Sass、postCSS是css处理领域的大杀器。参考文献[1]browserslist:https://github.com/browserslist/browserslist[2]caniuse:https://caniuse.com/#search=[3]astexplorer:https://astexplorer.net/[4]postcss-focus:https://www.npmjs.com/package/postcss-focus[5]postcss-focus源码:https://github.com/postcss/postcss-focus/blob/master/index.js[6]插件创建文档:https://github.com/postcss/postcss/blob/main/docs/writing-a-plugin.md[7]create-postcss-plugin:https://github.com/csstools/create-postcss-plugin[8]postcss-functions:https://www.npmjs.com/package/postcss-functions[9]stylelint:https://github.com/stylelint/stylelint[10]css模块:https://github.com/madyankin/postcss-modules[11]autoprefixer:https://github.com/postcss/autoprefixer[12]postcss-html:https://github.com/gucong3000/postcss-htmlhttps://github.com/gucong3000/postcss-htmlgithub.com/gucong3000/postcss-html