PPxu

通过 PostCSS 和 cssnext 探索书写未来 CSS

原文链接: bigbitecreative.com

通过 PostCSS 和 cssnext 探索书写未来 CSS

  • Mark Goodyear

Mark Goodyear

  • June 9th 2015

被 Twitter、Google 和 BBC 喜爱并使用,我决定通过 PostCSS 探索 CSS 后处理器的世界,看看它究竟是什么。

PostCSS 是什么?

PostCSS 是一个 CSS _后_ 处理器,它使用 JavaScript 插件来转换 CSS。就它本身而言,PostCSS 不会对你的 CSS 做任何处理;你需要添加一些 插件 才能开始使用。这使得它不仅仅具有模块化,而且非常强大。

它的工作原理是解析 CSS,然后将它转换成一棵 CSS 节点树,这样就能使用 JavaScript 来操作它 (这里就是插件起作用的地方)。然后它会返回修改过的节点树并保存起来。它和 Sass (以及其他预处理器) 的实现方式不一样,它们在本质上是用一种不同的语言书写然后编译成 CSS。

预 / 后处理什么?

为了用简单的方式解释预处理器和后处理器的差异,我举一个单位转换的例子。如果用 Sass 写,我们可以用一个函数将 px 值转换到 rem

/* 输入 */
.selector { margin-bottom: rem(20px); }

/* 输出, 假设基础的字体大小是 1rem */
.selector { margin-bottom: 1.25rem; }

这个很赞,给我们节约了手动计算的时间。使用 PostCSS,我们能做到更好。因为它是后处理 CSS,我们不需要调用任何函数来编译成 CSS。我们只需要简单的写 px 值,它就会自动处理成 rem

/* 输入 */
.selector { margin-bottom: 20px; }

/* 输出, 假设基础的字体大小是 1rem */
.selector { margin-bottom: 1.25rem; }

PostCSS 会处理每一条声明,每遇到一个 px 值,就会执行一个计算来将它转换成 rem 值。注意:由于它的模块化特性,你需要使用 postcss-pxtorem 插件来启用它。

使用 cssnext 书写未来 CSS

We can take advantage of proposed additions to the CSS spec into our stylesheets by leveraging cssnext—a PostCSS pack—which contains multiple plugins for features such as custom properties and custom selectors: 我们可以使用 cssnext 在我们的样式表中引入那些已经被提议加入 CSS 规范的特性,这是一个 PostCSS 的包,它包含了多个插件,支持类似 自定义属性自定义选择器 等特性:

/* 自定义特性 */
:root {
  --heading-color: #ff0000;
}

/* 自定义选择器 */
@custom-selector :--headings h1, h2, h3, h4, h5, h6;

/* 用法 */
:--headings {
  color: var(--heading-color);
}

通过 cssnext,这个会被处理成:

h1,
h2,
h3,
h4,
h5,
h6 {
  color: #ff0000;
}

这真的是非常整洁 —— 它们还给 CSS 带来了一些令人激动的特性。另外由于我们写的是符合未来规范的 CSS,当浏览器支持它们的时候,PostCSS 的构建步骤就不再需要了。你可以在 这里 找到完整的支持特性范围。

接下来,我来研究下如何使用 PostCSS 和我们自定义的函数来扩展 CSS 的能力。

向前一步,使用自定义函数扩展 CSS

我们可以使用 JavaScript 创建自定义函数来操作解析出来的 CSS。回到 Sass,我们用的很多的一个函数是生成一个间距单位 —— 通过基础的 line-height 计算得到 —— 帮助生成一个简单可维护的垂直间距:

$line-height: 32px;

/* 垂直间距函数 */
@function vr($amount) {
  @return $line-height * $amount;
}

/* 输入 */
.selector { margin-bottom: vr(2) }

/* 输出 */
.selector { margin-bottom: 64px; }

使用 PostCSS 来实现,我们可以做的很多,创建我们自己的 CSS 单位,而不是调用一个函数:

/* 输入 */
.selector { margin-bottom: 2vr }

/* 输出 */
.selector { margin-bottom: 64px; }

我非常喜欢这种方式,它用起来更加自然,就像我们在写原生的 CSS。这个效果背后的 JavaScript 是:

var vr = function (css) {
  var lineHeight = 32;

  css.eachDecl(function (decl) {
    if (decl.value.match(/vr/)) {
      decl.value = lineHeight * parseFloat(decl.value) + 'px';
    }
  });
};

This may seem a little more work, but if you’re not familiar with JavaScript, theres not actually that much happening. Here’s a commented version to dissect the function: 这个看起来可能有一些复杂,如果你对 JavaScript 不是很熟悉的话,实际上并没有做很多操作。这儿有一个带注释的版本来详细说明这个函数:

// 定义可以引入到 PostCSS 转换列表的函数,传入 CSS 节点树。
var vr = function (css) {

  // 设置 line-height.
  var lineHeight = 32;

  // 使用 PostCSS 的 `eachDecl()` 遍历每个 CSS 声明,并传入声明字符串 (`decl`)。
  css.eachDecl(function (decl) {

    // 如果声明值匹配到了 `vr` 单位。
    if (decl.value.match(/vr/)) {

      // 使用计算值替换声明值。
      // 使用 parseFloat() 移除 `vr` 单位并返回一个数字。
      decl.value = lineHeight * parseFloat(decl.value) + 'px';
    }
  });
};

对这些感兴趣的,我把这个做成了一个更高级的插件,无需配置,只要加到 PostCSS 的转换列表: postcss-vertical-rhythm

这只是一个很小的例子来说明用 PostCSS 可以做什么。希望现在你已经了解到它有多么强大,以及它和预处理器之间的差异。我推荐阅读这篇 文档插件列表 来看看有哪些已经可以用到你的项目中。

速度

PostCSS 声称它比其他预处理器快 3-30 倍。出于好奇,我在 10,000 个选择器上分别运行上面的 Sass 函数和 PostCSS 函数,每个方法用了5个属性。一共需要处理 50000 个东西。结果如下:

Libsass 3.2:

libsass

PostCSS:

postcss

结果出乎意料,PostCSS 处理这个函数比 Sass 快了几乎有 13 秒。这是不可思议的 34 倍。当然这不是一个真实的项目场景,不过这是一个有趣的测试,可以看到它的性能有多好 —— 我们面对一个新工具最不想要的就是降低构建的速度。Libsass 据说要在 3.3 版本里 提高速度,我要看看当它发布后究竟提高了多少。

思考

PostCSS 看起来非常有前途。我喜欢它的模块化,这样我可以任意插入我需要的,并且创建任何我需要它完成的自定义方法。可以尝试未来的 CSS 语法也非常令人兴奋。

虽然这么说,我不是指我们应该抛弃 Sass。Sass 是一个很棒的工具,我仍然会把它推荐给所有准备开始学预处理器的人。如果你想尝试一些更激进的,我认真的推荐来试试 PostCSS。

我希望听到所有人在实际项目中使用 PostCSS 的想法和经验。请联系我!

你可能还会喜欢