Skip to content

PostCSS

第一章:初识

一、简介

1. CSS 后处理器

自己写的 CSS 代码,想让 CSS 兼容各种浏览器、压缩、编写 CSS 时对代码规范检查等。所以就有了 CSS 后处理器。

CSS 后处理器是在原生的 CSS 代码的基础上面做处理,常见的处理工作如下:

  1. 兼容性处理:自动添加浏览器前缀(如 -webkit-、-moz- 和 -ms-)以确保跨浏览器兼容性。这种后处理器的一个典型例子是 autoprefixer
  2. 代码优化与压缩:移除多余的空格、注释和未使用的规则,以减小 CSS 文件的大小。例如,cssnano 是一个流行的 CSS 压缩工具。
  3. 功能增强:添加新的 CSS 特性,使开发者能够使用尚未在所有浏览器中实现的 CSS 功能。例如,PostCSS 是一个强大的 CSS 后处理器,提供了很多插件来扩展 CSS 的功能。
  4. 代码检查与规范:检查 CSS 代码的质量,以确保代码符合特定的编码规范和最佳实践。例如,stylelint 是一个强大的 CSS 检查工具,可以帮助你发现和修复潜在的问题。

PostCSS 本身只是实现了对 CSS 代码的 AST 功能,具体代码转换处理还需要借助插件或预设。

2. 是什么

PostCSS 是一个基于 JavaScript 转换 CSS 的工具。用于对 CSS 代码进行转换和处理。它通过插件系统提供了强大的功能,例如自动添加浏览器前缀、优化代码、支持现代 CSS 特性等。PostCSS 不仅可以作为独立工具使用,还可以与其他预处理器(如 Sass 或 Less)结合使用。

总结:CSS 语法转换的工具。

这一点有点像 webpack,webpack 本身仅做依赖分析、抽象语法树分析,其他的操作是靠插件和加载器完成的。

文档

官方文档:PostCSS

中文文档:PostCSS 中文网

插件市场:postcss/docs/plugins.md

3. 安装

PostCSS 是基于 node 编写的,因此可以使用 npm 安装。

bash
npm i -D postcss

PostCSS 库提供了对应的 js api 用于转换代码,如果你想使用 PostCSS 的一些高级功能,或者想开发 PostCSS 插件,就要 API 使用 PostCSS,API 的文档地址是:http://api.postcss.org

不过绝大部分时候,我们都是使用者,并不希望使用代码的方式来使用 PostCSS。

因此,我们可以再安装一个 postcss-cli,通过命令行来完成编译。

bash
npm i -D postcss-cli

postcss-cli 提供一个命令,它调用 PostCSS 中的 API 来完成编译。

bash
postcss 源码文件 -o 输出文件

更多命令查看 postcss-cli

第二章:配置文件

一、位置

postcss 有自己的配置文件,该配置文件会影响 postcss 的某些编译行为。

配置文件的默认名称:postcss.config.js

当使用 postcss-cli 或者构建工具(webpack、vite)来进行集成的时候,postcss 会自动加载配置文件。

javascript
module.exports = {
  map: false, // 关闭source-map
  plugins: [
    require("autoprefixer")({
      overrideBrowserslist: "last 10 versions",
    }),
  ],
}

postcss 配置文件最主要的其实就是做插件的配置。postcss 官网没有提供配置文件相关的文档,但是我们可以在:postcss/postcss-load-config 这个地方看到 postcss 配置文件所支持的配置项目。

二、配置项目

plugins:一个数组,里面包含要使用到的 postcss 的插件以及相关的插件配置。

javascript
module.exports = {
  plugins: [
    require("autoprefixer"),
    require("cssnano")({ preset: "default" }),
  ],
};

map:是否生成源码映射,对应的值为一个对象

javascript
module.exports = {
  map: { inline: false },
  plugins: [/* Your plugins here */],
};

默认值为 false,因为源码映射一般是会单独存放在一个文件里面。

syntax:用于指定 postcss 应该使用的 CSS 语法,默认情况下 postcss 处理的是标准的 CSS,但是有可能你的 CSS 是使用预处理器来写的,这个时候 postcss 是不认识的,所以这个时候需要安装对应的插件并且在配置中指明 syntax

javascript
module.exports = {
  syntax: "postcss-scss",
  plugins: [/* Your plugins here */],
};

安装 postcss-scss 这个插件,并且在配置文件中指定 syntax 为 postcss-scss,之后 PostCSS 就能够认识你的 sass 语法。

parser:配置自定义解析器。Postcss 默认的解析器为 postcss-safe-parser,负责将 CSS 字符串解析为 CSS AST,如果你要用其他的解析器,那么可以配置一下。

javascript
const customParser = require("my-custom-parser");

module.exports = {
  parser: customParser,
  plugins: [/* Your plugins here */],
};

stringifier:自定义字符串化器。用于将 CSS AST 转回 CSS 字符串。如果你要使用其他的字符串化器,那么也是可以在配置文件中国呢进行指定的。

javascript
const customStringifier = require("my-custom-stringifier");

module.exports = {
  stringifier: customStringifier,
  plugins: [/* Your plugins here */],
};

最后还剩下两个配置项:from、to,这两个选项官方是不建议你配置的,而且你配置的大概率还会报错,报错信息如下:

Config Error: Can not set from or to options in config file, use CLI arguments instead

这个提示的意思是让我们不要在配置文件里面进行配置,而是通过命令行参数的形式来指定。

至于为什么,官方其实解释得很清楚了:

In most cases options.from && options.to are set by the third-party which integrates this package (CLI, gulp, webpack). It's unlikely one needs to set/use options.from && options.to within a config file.

因为在实际开发中,我们更多的是会使用构建工具(webpack、vite),这些工具会去指定入口文件和出口文件。

第三章:常见插件

光使用 postcss 是没有多少意义的,要让它真正的发挥作用,需要插件。

插件市场:PostCSS Plugins

一、postcss-preset-env

为什么要有?

过去使用 postcss 的时候,往往会使用大量的插件,它们各自解决一些问题。

这样导致的结果是安装插件、配置插件都特别的繁琐。

于是出现了这么一个插件 postcss-preset-env,它称之为 postcss 预设环境,大意就是它整合了很多的常用插件到一起,并帮你完成了基本的配置,你只需要安装它一个插件,就相当于安装了很多插件了。

作用

postcss-preset-env 主要就是让开发者可以使用最新的的 CSS 语法,同时为了兼容会自动的将这些最新的 CSS 语法转换为旧版本浏览器能够支持的代码。

postcss-preset-env 的主要作用是:

  1. 让你能够使用最新的 CSS 语法,如:CSS Grid(网格布局)、CSS Variables(变量)等。
  2. 自动为你的 CSS 代码添加浏览器厂商前缀,如:-webkit-、-moz- 等。
  3. 根据你的浏览器兼容性需求,将 CSS 代码转换为旧版浏览器兼容的语法。
  4. 优化 CSS 代码,如:合并规则、删除重复的代码等。

快速使用

1)安装。

bash
pnpm add postcss-preset-env -D

2)在 postcss 配置中加入下面的配置。

javascript
module.exports = {
  plugins: {
    "postcss-preset-env": {} // {} 中可以填写插件的配置
  }
}

该插件的功能很多,下面一一介绍。

1. 自动的厂商前缀

某些新的 css 样式需要在旧版本浏览器中使用厂商前缀方可实现。

例如:

css
::placeholder {
  color: red;
}

该功能在不同的旧版本浏览器中需要书写为。

css
::-webkit-input-placeholder {
  color: red;
}
::-moz-placeholder {
  color: red;
}
:-ms-input-placeholder {
  color: red;
}
::-ms-input-placeholder {
  color: red;
}
::placeholder {
  color: red;
}

要完成这件事情,需要使用 autoprefixer 库。

postcss-preset-env 内部包含了该库,自动有了该功能。

如果需要调整兼容的浏览器范围,可以通过下面的方式进行配置。

方式 1:在 postcss-preset-env 的配置中加入 browsers

javascript
module.exports = {
  plugins: {
    "postcss-preset-env": {
      browsers: [
        "last 2 version",
        "> 1%"
      ]
    } 
  }
}

方式 2【推荐】:添加 .browserslistrc 文件

创建文件 .browserslistrc,填写配置内容。

last 2 version
> 1%

方式 3【推荐】:在 package.json 的配置中加入 browserslist

json
"browserslist": [
  "last 2 version",
  "> 1%"
]

browserslist 是一个多行的(数组形式的)标准字符串。

它的书写规范多而繁琐,详情见:https://github.com/browserslist/browserslist

一般情况下,大部分网站都使用下面的格式进行书写。

last 2 version
> 1% in CN
not ie <= 8
  • last 2 version:浏览器的兼容最近期的两个版本。
  • > 1% in CN:匹配中国大于 1% 的人使用的浏览器, in CN 可省略。
  • not ie <= 8:排除掉版本号小于等于 8 的 IE 浏览器。

默认情况下,匹配的结果求的是并集。

你可以通过网站:https://browserl.ist/ 对配置结果覆盖的浏览器进行查询,查询时,多行之间使用英文逗号分割。

browserlist 的数据来自于 CanIUse 网站,由于数据并非实时的,所以不会特别准确。

2. 未来的 CSS 语法

CSS 的某些前沿语法正在制定过程中,没有形成真正的标准,如果希望使用这部分语法,为了浏览器兼容性,需要进行编译。

过去,完成该语法编译的是 cssnext 库,不过有了 postcss-preset-env 后,它自动包含了该功能。

你可以通过 postcss-preset-env 的 stage 配置,告知 postcss-preset-env 需要对哪个阶段的 css 语法进行兼容处理,它的默认值为 2。

javascript
module.exports = {
  map: false, // 关闭source-map
  plugins: {
    "postcss-preset-env": {
      stage: 0 // 哪怕是处于草案阶段的语法,也需要转换
    }
  }
}

一共有 5 个阶段可配置:

  • Stage 0: Aspirational - 只是一个早期草案,极其不稳定。
  • Stage 1: Experimental - 仍然极其不稳定,但是提议已被 W3C 公认。
  • Stage 2: Allowable - 虽然还是不稳定,但已经可以使用了。
  • Stage 3: Embraced - 比较稳定,可能将来会发生一些小的变化,它即将成为最终的标准。
  • Stage 4: Standardized - 所有主流浏览器都应该支持的 W3C 标准。

了解了以上知识后,接下来了解一下未来的 css 语法,尽管某些语法仍处于非常早期的阶段,但是有该插件存在,编译后仍然可以被浏览器识别。

1)变量

未来的 css 语法是天然支持变量的。

:root{} 中定义常用变量,使用 -- 前缀命名变量。

css
:root {
  --lightColor: #ddd;
  --darkColor: #333;
}

a {
  color: var(--lightColor);
  background: var(--darkColor);
}

编译后,仍然可以看到原语法,因为某些新语法的存在并不会影响浏览器的渲染,尽管浏览器可能不认识。
如果不希望在结果中看到新语法,可以配置 postcss-preset-env 的 preserve 为 false。

2)自定义选择器
css
@custom-selector :--heading h1, h2, h3, h4, h5, h6;
@custom-selector :--enter :focus,:hover;

a:--enter{
  color: #f40;
}

:--heading{
  font-weight:bold;
}

:--heading.active{
  font-weight:bold;
}

编译后

css
a:focus,a:hover{
  color: #f40;
}

h1,h2,h3,h4,h5,h6{
  font-weight:bold;
}

h1.active,h2.active,h3.active,h4.active,h5.active,h6.active{
  font-weight:bold;
}
3)嵌套

与 LESS 相同,只不过嵌套的选择器前必须使用符号 &。

less
.a {
  color: red;
  & .b {
    color: green;
  }

  & > .b {
    color: blue;
  }

  &:hover {
    color: #000;
  }
}

编译后

css
.a {
  color: red
}

.a .b {
  color: green;
}

.a>.b {
  color: blue;
}

.a:hover {
  color: #000;
}

二、postcss-apply

该插件可以支持在 css 中书写属性集。

类似于 LESS 中的混入,可以利用 CSS 的新语法定义一个 CSS 代码片段,然后在需要的时候应用它。

less
:root {
  --center: {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  };
}

.item {
  @apply --center;
}

编译后

css
.item {
  position: absolute;
  left: 50%;
  top: 50%;
  -webkit-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
}

实际上,该功能也属于 cssnext,不知为何 postcss-preset-env 没有支持。

三、postcss-color-function

该插件支持在源码中使用一些颜色函数。

less
body {
  /* 使用颜色#aabbcc,不做任何处理,等同于直接书写 #aabbcc */
  color: color(#aabbcc);
  /* 将颜色#aabbcc透明度设置为90% */
  color: color(#aabbcc a(90%));
  /* 将颜色#aabbcc的红色部分设置为90% */
  color: color(#aabbcc red(90%));
  /* 将颜色#aabbcc调亮50%(更加趋近于白色),类似于less中的lighten函数 */
  color: color(#aabbcc tint(50%));
  /* 将颜色#aabbcc调暗50%(更加趋近于黑色),类似于less中的darken函数 */
  color: color(#aabbcc shade(50%));
}

编译后

css
body {
  /* 使用颜色#aabbcc,不做任何处理,等同于直接书写 #aabbcc */
  color: rgb(170, 187, 204);
  /* 将颜色#aabbcc透明度设置为90% */
  color: rgba(170, 187, 204, 0.9);
  /* 将颜色#aabbcc的红色部分设置为90% */
  color: rgb(230, 187, 204);
  /* 将颜色#aabbcc调亮50%(更加趋近于白色),类似于less中的lighten函数 */
  color: rgb(213, 221, 230);
  /* 将颜色#aabbcc调暗50%(更加趋近于黑色),类似于less中的darken函数 */
  color: rgb(85, 94, 102);
}

四、purgecss

该插件专门用于移除没有使用到的 CSS 样式的工具,相当于是 CSS 版本的 tree shaking(树摇),它会找到你文件中实际使用的 CSS 类名,并且移除没有使用到的样式,这样可以有效的减少 CSS 文件的大小,提升传输速度。

官网地址:PurgeCSS

安装该插件:

bash
pnpm add @fullhuman/postcss-purgecss -D

接下来我们在 src 下面创建一个 index.html,书写如下的代码:

html
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="./index.css">
</head>
<body>
  <div class="container">
    <div class="box1"></div>
  </div>
</body>

该 html 只用到了少量的 CSS 样式类。

目前我们的 index.css 代码如下:

css
@import "a.css";
@import "b.css";

.a {
  color: red;

  &.b {
    color: green;
    transform: translate(100px);
  }

  &>.b {
    color: blue;
  }

  &:hover {
    color: #000;
  }
}

.container {
  font-size: 20px;
}

p {
  color: red;
}

接下来我在 postcss.config.js 配置文件中引入 @fullhuman/postcss-purgecss 这个插件,具体的配置如下:

javascript
module.exports = {
  plugins: [
    require("postcss-import")({
      path: ["src/css"]
    }),
    require("postcss-preset-env")({
      stage: 2,
    }),
    require("@fullhuman/postcss-purgecss")({
      content: ['./src/**/*.html']
    })
  ],
};

我们引入 @fullhuman/postcss-purgecss 插件后,还做了 content 这个配置项目的相关配置,该配置项表示我具体的参照文件。也就是说,CSS 样式类有没有用上需要有一个具体的参照文件。

最终编译出来的结果如下:

css
.box1 {
  background-color: red;
}
.container {
  font-size: 20px;
}

@fullhuman/postcss-purgecss 这个插件除了 content 这个配置项,还有一个配置项也非常的常用:

  • safelist:可以指定一个字符串的值,或者指定一个正则表达式,该配置项目所对应的值(CSS 样式规则)始终保留,即便在参照文件中没有使用到也需要保留
javascript
const purgecss = require('@fullhuman/postcss-purgecss');

module.exports = {
  plugins: [
    // 其他插件...
    purgecss({
      content: ['./src/**/*.html', './src/**/*.js'],
      safelist: [/^active-/],
    }),
  ],
};

safelist 所对应的值的含义为:匹配 active- 开头的类名,这些类名即便在项目文件中没有使用到,但是也不要删除。

[扩展] postcss-import

该插件可以让你在 postcss 文件中导入其他样式代码,通过该插件可以将它们合并。

1)安装

bash
pnpm add postcss-import -D

2)配置

javascript
module.exports = {
  plugins: [
    require("postcss-import"),
  ],
};

module.exports = {
  plugins: [
    require("postcss-import")({
      path: ["src/css"],
      plugins: [
        postcssNested(),
      ],
    }),
    // 其他插件...
  ],
};

一些配置项:

  • path:设置查找 CSS 文件的路径,默认为当前文件夹。
  • plugins:允许你指定在处理被 @import 引入的 CSS 文件时使用的其他 PostCSS 插件。这些插件将在 postcss-import 合并文件之前对被引入的文件进行处理,之后再进行文件的合并。

更多配置项 postcss/postcss-import

由于后续会将 postcss 加入到 webpack 中,而 webpack 本身具有依赖分析的功能,所以该插件的实际意义不大。

[扩展] stylelint

这个不依赖 postcss,只是它写了一个插件来兼容 postcss。

官网:https://stylelint.io/

在实际的开发中,我们可能会错误的或不规范的书写一些 css 代码,stylelint 会即时的发现错误。

由于不同的公司可能使用不同的 CSS 书写规范,stylelint 为了保持灵活,它本身并没有提供具体的规则验证。

你需要安装或自行编写规则验证方案。通常,我们会安装 stylelint-config-standard 库来提供标准的 CSS 规则判定。

bash
pnpm add stylelint stylelint-config-standard -D

这里安装了两个依赖:

  • stylelint:做 CSS 代码风格校验,但是具体的校验规则它是不知道了,需要我们提供具体的校验规则。
  • stylelint-config-standard:这是 stylelint 的一套校验规则,并且是一套标准规则。

安装好后,我们需要告诉 stylelint 使用该库来进行规则验证。

告知的方式有多种,比较常见的是使用文件 .stylelintrc

json
// .styleintrc
{
  "extends": "stylelint-config-standard"
}

怎么结合到 postcss 中?

javascript
module.exports = {
  map: false, // 关闭source-map
  plugins: {
    "postcss-preset-env": {
      stage: 0, //哪怕是处于草案阶段的语法,也需要转换
      preserve: false
    },
    "postcss-apply": {},
    "postcss-color-function": {},
    "stylelint": {}
  }
}

此时,如果你的代码出现不规范的地方,编译时将会报出错误。

css
body {
    background: #f4;
}

发生了两处错误:

  1. 缩进应该只有两个空格。
  2. 十六进制的颜色值不正确。

如果某些规则并非你所期望的,可以在配置中进行设置。

json
{
  "extends": "stylelint-config-standard",
  "rules": {
    "indentation": null
  }
}

设置为 null 可以禁用该规则,或者设置为 4,表示一个缩进有 4 个空格。具体的设置需要参见 stylelint 文档:Rules

检查出来的问题能否自动修复?只需要将 stylelint 插件的 fix 配置项配置为 true 即可。

json
// postcss 配置主要其实就是做插件的配置
module.exports = {
  plugins: [
    require("stylelint")({
      fix: true
    }),
  ],
};

但是这种错误报告需要在编译时才会发生,如果我希望在编写代码时就自动在编辑器里报错呢?

既然想在编辑器里达到该功能,那么就要在编辑器里做文章。

安装 vscode 的插件 stylelint 即可,它会读取你工程中的配置文件,按照配置进行实时报错。

实际上,如果你拥有了 stylelint 插件,可以不需要在 postcss 中使用该插件了。

第四章:自定义插件

一、编写插件步骤

在 PostCSS 官网,实际上已经介绍了如何去编写一个自定义插件:https://postcss.org/docs/writing-a-postcss-plugin

1)需要有一个模板

javascript
module.exports = (opts = {}) => {
  return {
    postcssPlugin: '插件名字',
    Once (root) {
      // 整个文件开始时只触发一次,因为文件只有一个 Root 根节点
    },
    Declaration (decl) {
      // 每当遍历到任何一个“属性声明”时都会触发,decl 就是当前遍历到的那个声明对象
    }
  }
}

// 必须有这句话来标识这是一个 PostCSS 插件
module.exports.postcss = true;

2)接下来就可以在插件里面添加一组监听器,对应的能够设置的监听器如下:

  • Root(根):AST 树的最高层节点,代表整个 CSS 文件。
  • AtRule(@规则):所有以 @ 开头的语句,比如 @charset "UTF-8"@media (screen) {}
  • Rule(规则):包含选择器和内部声明的代码块,比如 input, button {}
  • Declaration(声明):键值对,比如 color: black;
  • Comment(注释):独立存在的注释。注意,写在选择器或值中间的内联注释是被存在父节点的 raws 属性里的,而不是单独的节点。

二、具体示例

现在在我们的 src 中新建一个 my-plugin.js 的文件,代码如下:

javascript
module.exports = (opts = {}) => {
  // Plugin creator to check options or prepare caches
  return {
    postcssPlugin: "PLUGIN NAME",
    Declaration(decl) {
      console.log(decl.prop, decl.value)
    }
  };
};

module.exports.postcss = true;

在上面的代码中,我们添加了 Declaration 的监听器,通过该监听器能够拿到 CSS 文件中所有的声明。

接下来我们就可以对其进行相应的操作。

现在我们来做一个具体的示例:编写一个插件,该插件能够将 CSS 代码中所有的颜色统一转为十六进制。

这里我们需要使用到一个依赖包:color 该依赖就是专门做颜色处理的

bash
pnpm add color -D

之后通过该依赖所提供的 hex 方法来进行颜色值的修改,具体代码如下:

javascript
const Color = require("color");

module.exports = (opts = {}) => {
  // Plugin creator to check options or prepare caches
  return {
    postcssPlugin: "convertColorsToHex",
    Declaration(decl) {
      // 先创建一个正则表达式,提取出如下的声明
      // 因为如下的声明对应的值一般都是颜色值
      const colorRegex = /(^color)|(^background(-color)?)/;
      if (colorRegex.test(decl.prop)) {
        try {
          // 将颜色值转为 Color 对象,因为这个 Color 对象对应了一系列的方法
          // 方便我们进行转换
          const color = Color(decl.value);
          // 将颜色值转换为十六进制
          const hex = color.hex();
          // 更新属性值
          decl.value = hex;
        } catch (err) {
          console.error(
            `[convertColorsToHex] Error processing ${decl.prop}: ${error.message}`
          );
        }
      }
    },
  };
};

module.exports.postcss = true;

第五章:与构建工具结合

一、webpack 集成

官方文档:postcss-loader

我们的 css 代码,需要先交给 postcss 处理变成普通的 css,然后用 css-loader 变成 js,之后使用 style-loader 把 css 添加到 style 元素中。

1)安装依赖

bash
npm install --save-dev postcss-loader postcss

2)配置 webpack

javascript
const HtmlWebpackPlugin = require("html-webpack-plugin")

module.exports = {
  mode: "development",
  devtool: "source-map",
  module: {
    rules: [
      {
        test: /\.css$/, use: ["style-loader", "css-loader?modules", "postcss-loader"]
      }
    ]
  },
  devServer: {
    open: true
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./public/index.html"
    })
  ]
}

3)创建 postcss.config.js

preview
图片加载中
预览

Released under the MIT License.