深入 Webpack5 等构建工具系列二(10) - postcss-preset-env 和配置抽取

4,491 阅读9分钟

这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战

上篇文章我们讲到了 postcss-loader,可以通过它在 webpack 中使用 postcss,然后再借助 autoprefixer 插件来给 css 自动添加浏览器前缀。但是,你会发现很多脚手架中在配置自动添加浏览器前缀时使用 autoprefixer 的比较少,更多的是使用另一个插件——postcss-preset-env,下面,我们要讲的就是 postcss-preset-env 这个插件。

首先来看 GitHub 上关于 postcss-preset-env 的介绍:

PostCSS Preset Env lets you convert modern CSS into something most browsers can understand, determining the polyfills you need based on your targeted browsers or runtime environments.

翻译过来的意思就是 PostCSS Preset Env 可以将现代 CSS 转换为大多数浏览器可以理解的内容,并根据目标浏览器或运行时环境确定所需的 polyfill

注:这里的 modern CSS 的指的是现代的 CSS 特性,对于现代的 CSS 特性,有的浏览器支持,而有的浏览器是不支持的。另外,关于 polyfill,我们到后面讲 babel 时再讲。

1. postcss-preset-env

  • 事实上,在配置 postcss-loader 时,我们配置插件并不需要使用 autoprefixer
  • 我们可以使用另外一个插件:postcss-preset-env
    • postcss-preset-env 也是一个 postcss 的插件;
    • 它可以帮助我们将一些现代的 CSS 特性,转成大多数浏览器认识的 CSS,并且会根据目标浏览器或运行时环境添加所需的 polyfill
    • 也包括会自动帮助我们添加 autoprefixer(所以相当于已经内置了 autoprefixer);

那现代的 CSS 特性有哪些呢?我们下面就拿 8 位的十六进制颜色码举例。

通常,我们使用的十六进制颜色码是 6 位的(# 后面跟 6 位十六进制数,如 #123456),第 12 位代表 rred),第 34 位代表 ggreen),第 56 位代表 bblue)。但事实上还有一种写法,即使用 8 位的十六进制颜色码(如 #12345678),最后两位代表 aalpha 透明度),这样来写后,最后的两位会生效吗?答案是有些浏览器会生效,有些则不会,这与浏览器能否正常解析 8 位的十六进制颜色码有关。

下面,我们可以来到项目的 ./src/css/index.css 路径下,修改 index.css 文件中的 color 属性值如下:

image-20210206122934947.png

.content {
  color: #12345678;
}

然后运行 npm run build 命令重新对代码进行编译,编译好后打开浏览器查看效果:

image-20210206123434896.png

可以看到,在当前版本的 Google Chrome 浏览器中,字体的颜色确实已经发生了变化,并且,在控制台的 Elements 标签页下可以看到,浏览器解析出来的 color 属性值依然是 8 位十六进制颜色码格式的值。这意味着,对于 8 位十六进制颜色码的值,我们编写时是什么样的,最后打包的结果就是什么样的,浏览器使用时也就是什么样的。这就有一个问题,对于我们当前版本的 Google Chrome 浏览器是可以正确识别这个样式,但是对于有些浏览器很可能是不支持这种 8 位十六进制颜色码的,导致的结果就是可能会报错也可能什么效果都没有。

所以,我们希望,当我们在项目中使用了诸如 8 位十六进制颜色码等现代 CSS 特性时,postcss 能帮我们将其转换为大多数浏览器可以识别的格式(比如这里的 8 位十六进制颜色码转成 rgba() 格式)。

那我们之前使用的 autoprefixer 能帮我们实现这个效果吗?答案是否定的,因为我们已经看到,autoprefixer 插件只能帮助我们添加浏览器前缀,而对于现代 CSS 特性的转换它是做不到的。这时,我们就可以使用 postcss-preset-env 插件来实现对现代 CSS 特性的转换了。

要想使用 postcss-preset-env,我们首先需要安装它,因为它也是个独立的插件,安装命令如下:

npm install postcss-preset-env -D

1.1 直接在 webpack 配置文件中配置 postcssOptions

安装完成后,再来到 webpack 的配置文件中进行修改(在 postcssOptions.plugins 中新增一项:通过 require 引入 postcss-preset-env):

image-20210206141736649.png

use: [
  // 注意:执行顺序(从下往上,从右往左,从后往前)
  'style-loader',
  'css-loader',
  {
    loader: 'postcss-loader',
    options: {
      postcssOptions: {
        plugins: [
          require('autoprefixer'),
          require('postcss-preset-env')
        ]
      }
    }
  }
],

然后,我们再来运行 npm run build,看下效果:

image-20210206142749927.png

可见,通过使用 postcss-preset-env 这个插件,webpack 编译出来的 css 颜色样式就有了转换,8 位十六进制颜色码被转换成了 rgba() 格式的值,而 rgba() 的话大多数浏览器都是支持的。

因此,开发中,为了使用一些现代 CSS 特性,更多情况下,我们会使用 postcss-preset-env 这个插件,而不是 autoprefixer 这个插件。

此外,由于 postcss-preset-env 中已经包含了 autoprefixer 的特性(即前者已经做了后者要做的事了),我们只使用 postcss-preset-env 即可,无需再添加 autoprefixerautoprefixer 有没有都可以)。所以配置文件可以简化如下:

image-20210206143618178.png

use: [
  // 注意:执行顺序(从下往上,从右往左,从后往前)
  'style-loader',
  'css-loader',
  {
    loader: 'postcss-loader',
    options: {
      postcssOptions: {
        plugins: [
          require('postcss-preset-env')
        ]
      }
    }
  }
],

只使用 postcss-preset-env 插件后,我们再运行 npm run build 命令来验证一下,效果如下:

image-20210206144314129.png

可见,颜色样式格式的转换和浏览器前缀的添加都没问题。

以上,就是关于 postcss-preset-env 插件的使用,以后我们想要添加浏览器前缀以及使用现代 CSS 特性时,只使用这个插件就可以了。这也是为什么很多脚手架里使用这个插件,而不是 autoprefixer 插件的原因(postcss-preset-env 中已经包含了 autoprefixer 的特性了)。

两种等价的写法

需要补充说明的是,上面在配置插件时,下面两种写法是等价的:

plugins: [
  require('postcss-preset-env')
]
plugins: [
  'postcss-preset-env'
]

2 种写法中直接使用字符串形式,到时会自动转换成使用第 1 种写法中的 require 进行引入。'postcss-preset-env' 相当于 require('postcss-preset-env') 的简写。

但是,并不是所有的插件都可以像上面那样简写,autoprefixerpostcss-preset-env 可以简写是因为它们不需要传任何参数,而如果某些插件在调用时需要传一些参数时(比如 require('abc')({}),具体怎么传参需要查阅对应插件的官方文档),就不能直接使用字符串简写了。

1.2 使用配置文件配置 postcssOptions

下面,我们再来思考一个问题:如果需要让 postcss 作用在多种格式的文件上时,我们该怎么配置呢?比如我们还希望 postcssless 文件也进行处理。那是不是意味着匹配 less 文件的规则对象中也要配置一遍匹配 css 文件的规则对象中的相应配置呢?就像下面这样配置:

image-20210206151958561.png

因为对于 less 文件,我们是先通过 less-loader 使用 lessless 文件转换成 css 文件,然后可以通过 postcsscss 进行进一步处理,接着使用 css-loader 来解析加载 css 代码,最后通过 style-loader 将解析后的 css 代码插入到页面中。

你可能会发现如果像上面这样做,匹配 css 文件的规则对象中的 postcss 的配置和匹配 less 文件的规则对象中的 postcss 的配置是相同的,能不能将这一配置信息单独进行抽离呢?答案是可以的。

如果我们想对 postcss-loader 的配置单独做一个抽离的话,可以这样做:

image-20210206204052186.png

即把 postcss-loader 对应的 UseEntry 对象都替换为 postcss-loader 字符串,同时在项目的根目录下创建 postcss.config.js 文件(注意:该文件名不能乱起,因为到时候会根据这个名字来读这个文件,有效的文件名详见官方文档),在该文件中通过 CommonJS 的方式导出一个对象,对象中再配置要使用的插件, postcss.config.js 文件的内容如下:

image-20210206205454689.png

module.exports = {
  plugins: [
    require('postcss-preset-env')
  ]
}

相当于在 webpack 的配置文件中用“postcss-loader”字符串替换掉了 postcss-loader 对应的 UseEntry 对象,同时把 UseEntry 对象的 options 属性中的 postcssOptions 的内容放到了 postcss.config.js 文件中(当然,上面代码块中的 require('postcss-preset-env') 也可以简写成 'postcss-preset-env')。

好,现在我们已经把 postcssOptions 中的内容单独抽离到一个配置文件里了,webpack 的配置文件中的相应位置也只需要直接写上 postcss-loader 就可以了,这样代码就显得更简洁了。

下面,我们再来运行 npm run build 命令对代码进行编译,然后去浏览器中查看效果,可以看到效果是一样的:

image-20210214104611740.png

而如果把 postcss.config.js 中的 require('postcss-preset-env') 注释后再运行 npm run build 进行编译,很多效果(8 位十六进制颜色码的转化、浏览器前缀的添加)就都没有了:

image-20210214104924288.png

可见,我们在 postcss.config.js 中编写的配置是有起作用的。

以上,讲了两种配置 postcssOptions 的方式,一种是直接在 webpack 的配置文件中进行配置,另一种是使用单独的配置文件进行配置。

总结一下,本文主要讲了以下几点:

  • postcss-preset-env 插件和 autoprefixer 插件的区别;
  • 在使用某些 postcss 插件时,也可以直接传入字符串;
  • 可以对 postcssOptions 的内容做抽离,配置进一个单独的 postcss 配置文件。

补充

问:postcss-preset-env 依赖 autoprefixer 吗?

  • 可以去 postcss-preset-env 对应的包下的 package.json 文件中查看 postcss-preset-env 有没有依赖 autoprefixer

image-20210214112050255.png

可见,postcss-preset-env 是需要依赖 autoprefixer 的。所以,就算之前没有安装 autoprefixer,在安装 postcss-preset-env 时,也会自动安装 autoprefixer