解剖postCSS- 向前端架构师迈出一小步

开发 前端
最近很火的TailwindCSS有一个功能,可以将项目未使用的css选择器从编译后css文件中移除。

 最近很火的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,希望通过此文让你对这款大杀器有更深的认识。

什么是postCSS

postCSS是一款css编译器。

类比Babel家族的@babel/parser可以将js代码解析为AST(抽象语法树),再利用众多插件(@babel/plugin-xx)的能力改写AST,最终输出改写后的js代码。

postCSS利用自身的parser可以将css代码解析为AST,再利用众多插件(上文介绍的autoprefixer就是一种)改写AST,最终输出改写后的css代码。


从这点就能看出其与Less这样的「css预处理器」的不同 —— postCSS的输入与输出产物都是css文件。

因此,postCSS也被成为「后处理器」,因为其通常在css处理链条的最后端。


postCSS的AST

你可以在astexplorer[3]中选择:

  • 语言:css
  • parser:postCSS

来了解postCSS如何解析css。

比如,对于如下css代码:

  1. /** 
  2.  * I am KaSong 
  3.  */ 
  4.  
  5. @media screen and (min-width: 900px) { 
  6.   article { 
  7.     padding: 1rem 3rem; 
  8.   } 
  9.  
  10.  
  11. ul { 
  12.  margin: 3rem; 
  13.  
  14. ul li { 
  15.  padding: 5px; 

会被postCSS解析为如下树结构的AST:


节点有如下几种类型:

  • Root:根节点,代表一个css文件
  • AtRule:以@开头的申明,比如@charset "UTF-8"或@media (screen) {}
  • Rule:内部包含定义的选择器,比如input, button {}
  • Declaration:key-value键值对,比如color: black;
  • Comment:单独的注释。selectors、at-rule的参数以及value的注释在节点的node属性内

实现一个简单的插件

接下来我们从一个插件的实现来了解开发者如何介入postCSS编译流程。

postcss-focus[4]会为所有:hover选择器增加:focus以提高键盘操作的可用性。

对于如下代码:

  1. .a:hover, .b:hover, .c:hover { 
  2.   opacity: .5; 

经过该插件处理后会输出:

  1. .a:hover, .b:hover, .c:hover, .a:focus, .b:focus, .c:focus { 
  2.   opacity: .5; 

你可以安装postcss、postcss-focus后通过如下demo在控制台看到结果:

  1. const postcssFocus = require('postcss-focus'); 
  2. const postcss = require('postcss'); 
  3. const fs = require('fs'); 
  4.  
  5. // 输入的css文件地址 
  6. const from = 'src/a.css'
  7. const to = 'output/a.css'
  8.  
  9. fs.readFile(from, (err, css) => { 
  10.   postcss(postcssFocus).process(css, { fromto }).then(result => { 
  11.     console.log(result.css) 
  12.   }) 
  13.    
  14. }) 

接下来我们分析postcss-focus源码[5]的实现逻辑:

  1. postCSS将输入的css解析为AST
  2. 遍历AST中所有Rule类型节点
  3. 维护一个数组,遍历这个节点的所有selector,每遍历到一个包含:hover的selector就往数组中push一个:focus的selector
  4. 将2中得到的数组concat到该节点已有的selectors后
  5. 根据改变后的AST输出新的css

核心源码如下:

  1.   postcssPlugin: 'postcss-focus'
  2.   // 步骤1 
  3.   Rulerule => { 
  4.     // 步骤2 
  5.     if (rule.selector.includes(':hover')) { 
  6.       let focuses = [] 
  7.       for (let selector of rule.selectors) { 
  8.         if (selector.includes(':hover')) { 
  9.           let replaced = selector.replace(/:hover/g, ':focus'
  10.           if (!hasAlready(rule.parent, replaced)) { 
  11.             focuses.push(replaced) 
  12.           } 
  13.         } 
  14.       } 
  15.       // 步骤3 
  16.       if (focuses.length) { 
  17.         rule.selectors = rule.selectors.concat(focuses) 
  18.       } 
  19.     } 
  20.   } 

这个插件只是为了演示插件的基本工作方法,实际上该插件实现的比较粗糙。

postCSS提供了详细的插件创建文档[6]。甚至提供了create-postcss-plugin[7]用来创建插件的模版代码。

更多可能性

由于提供了表达、改写css AST的能力,postCSS的插件可以实现非常多功能。比如:

postcss-functions

上文介绍了Declaration节点表达「css属性」的键值对,其中值为「字符串」类型。

那么完全可以自定义值的解析规则。

  1. body { 
  2.   color: getColor(); 

通过定义getColor函数,并在AST中将其解析为函数执行,就能在css文件中用js写逻辑代码。

这就是postcss-functions[8]

stylelint

配置不同的lint规则,实现css的静态语法检测。这就是stylelint[9]

总结

当前postCSS插件按功能划分大体有如下几类:

  • 解决全局css问题,比如提供css module[10]支持
  • 使用未全面兼容的css特性,比如autoprefixer[11]
  • 格式化,提高css可读性
  • 图片和文字处理
  • linters,比如stylelint
  • 不同语法的css支持,比如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 module:

https://github.com/madyankin/postcss-modules[11]autoprefixer:

https://github.com/postcss/autoprefixer[12]postcss-html:

https://github.com/gucong3000/postcss-html

 

责任编辑:姜华 来源: 魔术师卡颂
相关推荐

2021-02-25 16:58:38

Babel前端JavaScript

2013-10-09 09:32:58

2016-11-07 13:31:24

2017-09-22 11:18:19

2009-12-17 17:40:45

架构师

2017-03-30 16:41:07

互联网

2018-03-15 13:14:59

思科网络技术智慧系统

2020-06-28 14:15:52

前端架构师互联网

2018-02-10 11:24:39

Python数据程序

2020-07-22 22:10:34

互联网物联网IOT

2012-10-22 10:01:45

TD-LTETD-LTE频谱TDD频谱

2012-09-06 13:12:41

架构师ArchSummit

2020-11-11 09:37:56

芯片

2021-05-14 05:26:25

前端架构开发

2012-06-17 12:58:04

架构师架构

2011-11-28 14:59:47

华为

2013-04-27 13:36:52

Chrome

2023-09-18 14:39:02

2022-01-04 10:19:23

架构运维技术

2017-03-07 15:54:13

华为
点赞
收藏

51CTO技术栈公众号