cover_image

浅谈Postcss处理流程并速写一个基础的Postcss插件

KaFan 三七互娱技术团队
2024年09月02日 10:01
01

前言

图片
图片

PostCss,想必很多前端开发者几乎都会在webpack配置中添加相关的postcss插件来处理css样式文件,比如打包的时候给css属性添加各浏览器兼容前缀(即垫片)、亦或是将px单位转换为rem单位等等,但普遍都是npm完对应的依赖包,在插件配置项plugins那里配置一下就完了,很少会去特地了解其处理css的原理,正好之前在图灵项目开发暗黑模式时需要编撰一个postcss插件来将已有的亮眼模式下的颜色转换出一套对应的暗黑颜色,让我又重拾起postcss进行相关原理的温习,以及去学习如何自己编撰相关的逻辑来处理像字体颜色、背景色的转换。

02

什么是PostCss

图片
图片

如官网所说,PostCss是一个用 JavaScript 转换 CSS 的工具,即插件的匹配规则、转化规则都是用JS语法来写的,总体而言可以概括为以下三点:
1、类似JS一样,它会把 CSS 文件的字符串解析成抽象语法树AST(Abstract Syntax Tree),解析过程中会检查 CSS 语法是否正确,不正确会给出错误提示;
2、执行插件函数(即你自己写的处理函数),PostCSS 本身不处理任何具体任务,但它提供了以特定属性或者规则命名的事件,有特定功能的插件(如 autoprefixer、CSS Modules)会注册事件监听器,所以你自己写的处理函数要定义注册在哪个节点或者匹配哪些特定属性时,执行对应的转化处理函数;
3、插件对 AST 处理后,PostCSS 会把处理过的 AST 对象重新转成 CSS string(也就是我们看到的CSS样式文件)
当然如果你传进去的插件函数没任何处理逻辑的话,甚至是个空函数,其过程就等于是CSS string -> AST -> CSS string,前后完全一样,只有我们附加插件并赋予处理逻辑,PostCss才具有其实用性、功能性。

03

认识CSS语法

图片
图片
图片

如上图:
1、选择符 - 代表此CSS样式要匹配应用的HTML元素
2、声明 - 包含一个 CSS 属性名称和一个值,以冒号分隔
3、声明块 - 由一句或多句声明构成,以分号分隔
4、属性 - 样式类型,比如是背景色、字体大小、间距等等
5、值 - 样式规则,比如什么颜色、什么大小、什么长度等等

04

CSS - AST语法树对象

图片
图片

Declaration 对象

图片

一个Declaration 对象代表 CSS 中的一条声明语句,它主要包含以下属性:

  • type - 标记当前对象的类型

    parent - 父对象的实例

    prop - 声明中的属性名

    value - 声明中的值

以上这四个比较常用到,其他的看下面例子注释即可
那接下来我就以一句声明语句"color:red"转换为例带大家认识下其转换为Declaration对象后的样子

.test {
color: red
}

转译成Declaration 对象即下面

图片

Rule 对象

图片

Rule 对象用于描述选择器,像刚刚的例子 .test { } 就是一个选择器,那我们首先也来看一下其主要属性

  • type - 对象的类型

    parent - 父对象的实例

    nodes - 子对象的实例

    selector - 选择器的选择符字符串(例子中为".test")

    其余属性解释见示例代码中的注释

还是以我们上面的
.test {
color: red
}
为例
其转换后的Rule对象为

图片

Root 对象

图片

顾名思义,根对象,即AST树最外层的对象,生动点讲的话一个样式文件转出来这个文件本身就是一个Root对象,那它的属性相对而言就比较少了,主要就是type和node

  • type - 当前对象的类型

    nodes - 子节点对应对象的实例

同样的例子转换后如下

图片

知道上面的nodes为啥是数组么,因为你一个样式文件下面可能有许许多多的选择器语句,不单单是上面一条.test { color: red }

AtRule 对象

图片

即语法带有@的CSS语句转化出来后的对象,你能想到的@语句有哪些?@import、@keyframe亦或是@media等等,那同样的AtRule对象主要有如下属性:

  • type - 当前对象的类型

    parent - 当前对象的父对象

    name - @紧跟着的语法词

    params name 值,即@xxxx abc里的abc

比如我们在CSS文件里用这样一句话引入外部另一个样式文件 @import url('./test.css'),它将被解析转换为

图片

Comment 对象

图片

Comment对象即CSS中的注释语句解析出来后的对象,例如 { color: red; // 字体颜色为红色 } 转换后为

图片

好了,以上就是CSS-AST树的五大对象,在知道打包编译的过程中CSS会被转换成上面的AST对象,那如何去加工、介入篡改它们你是不是就已经有点头绪了,就是要在转回css string前去拿到你要修改的CSS声明语句的对象,然后去修改对象中的值,那么修改后转换回css string后,原来的样式文件就被你篡改成你想要的了。OK那大体的思路是不是就已经明朗了,但是我们要如何去 get 到这些对象呢,这时就需要用到 PostCss 提供的事件监听器了。

05

事件监听器

图片
图片

PostCss给我们提供了两类事件监听器(以postcss8为版本前提)

  • 【以特定属性或者规则命名】的监听器 —— 我们这里暂称为【规则监听器】

图片
  • 【以时间节点或者执行状态命名】的监听器 —— 我们这里暂称为【钩子监听器】

图片

那PostCss会怎么去触发这些事件监听器呢,这里我把它的遍历流程写出来下

prepare -> Once -> 规则监听事件-> OnceExit

这里着重讲一下规则监听事件的执行

首先PostCss会以深度优先的方式遍历AST树,优先执行所有Root监听事件,第二步检查 Root 是否有子对象,如果有,则遍历子对象,执行子对象对应的事件监听器;如果没有子对象,则直接进入第三步,第三步会执行所有插件注册的 RootExit 事件监听器。插件注册的 Root、RootExit 事件的监听器只能是函数。所有的规则监听事件函数的第一个参数是当前访问的 AST 对象,第二个参数是 postcss 的 Result 对象和一些其他属性,通过 Result 对象可以获取 css string、opts 等信息,正常情况下我们写插件只需用到第一个参数即可。

以上就是PostCss的遍历过程中会触发的事件及钩子函数,当然postcss7的事件语法跟8有所不同,旧版本7则是以walk 、walkAtRules 、walkComments 、walkDecls 、walkRules去添加回调处理函数,具体怎么写我等下会放出个例子给大家看,很容易上手的版本7。

06

PostCss版本

图片
图片

目前主流版本是postcss7 和 postcss8,这俩版本的插件语法还是有略微变动的,所以在编写插件的时候最好还是要确定下自己用的postcss是哪个版本,不然插件会生效不了,自我感觉来说旧版本7的语法可读性会相对容易理解一点,但是新版本8更加轻量化了。那最后我将用我自己的一段postcss插件代码来演示下我如何将写死的字体单位转换为变量计算。

07

示例

图片
图片
图片

以上就是我的一个简单的PostCss插件的源码,相应的原理解释我也备注在代码里,大家看看即可,postcss8的话大家可以根据官方文档将语法变更过去即可,受限篇幅这里就不都列出来了,网上也有很多教学篇的。

08

总结

图片
图片

PostCss插件能大大提升我们的一些样式处理效率,特别是对于一些旧项目的样式改版或者后面要做一套统一的样式规范亦或是暗黑模式主题等,你都可以先按照你传统的快捷的写法先把CSS样式文件写好,然后再去配置一个PostCSS插件,把匹配规则、加工处理逻辑写好(例如转换单位、转换颜色等等),然后在打包编译的时候,其就会自动帮你转换成你想要的样子,这样是不是就避免了自己一个个网页、一个个样式文件、一句句CSS语句去改,大大缩短人工修改的时间以及出错的概率,当然也不是让插件处理后你就可以高枕无忧,你还是要检查下最终处理转换后的结果是否符合你的预期,以及处理出来的CSS优先级是否够高,是否会被其他样式覆盖亦或是影响到其他无关的样式布局等等。

好了,以上就是我对PostCss处理流程原理的一个浅浅的解读,当然源码的话自己还没去深探,后续有时间也可以再去研究研究,最后希望此篇文章能带你初步认识PostCss,以及如何去编撰一个属于自己的PostCss插件,更希望对你能有所帮助,谢谢~

图片

继续滑动看下一个
三七互娱技术团队
向上滑动看下一个