Skip to content

翻译计划-浅谈PostCSS

发表于:May 19, 2016 at 08:00 AM

CSS 的发展,像所有的语言一样,是一个不断迭代的过程。

出处

原始链接:An Introduction To PostCss 作者:Drew Minns 译者:Icarus

正文

CSS 的发展,像所有的语言一样,是一个不断迭代的过程。伴随着每一次重大的修改发布,我们都会有新的特性和语法来帮助我们去修改样式。CSS3 的提出,使我们能够设计之前只可能用 javascript 来完成的交互特性。每过一段时间就会有让我们编写样式更简单且更灵活的新工具浮现。 PostCss 是相对最新的并且被样式使用的工具之一。PostCss 目标是用一个用户插件和工具的生态系统彻底重构 CSS。使用与 Sass 和 LESS 这样的预处理器相同的规则,他将延伸出来的 CSS 语法和特性转换为现代的、对浏览器友好的 CSS。 你会问如何实现的?Javascript。 Javascript 有能力将我们的样式转化的比其他预处理器更块。使用诸如 Gulp 和 Grunt 的任务执行工具,我们可以将样式表在构建过程中转化,就像是 Sass 和 LESS 的编译。诸如 React 和 AngularJS 这样的 javascript 库或框架允许我们直接在 javascript 里面进行 CSS 的编写,为 javascript 转化我们的样式表开辟了途径。

PostCSS 的历史

PostCSS 作为 Autoprefixer 的作者 Andrey Sitnik 开发出来的工具,被正式发布作为 CSS 编译过程中使用 javascript 的方法。PostCSS 本身只有一个简单的 API,这个 API 在使用浩瀚无边的插件生态系统时,展现出非常强大的能力。为了提供有帮助的查错方法,PostCSS 会生成 source maps,而且一个抽象语法树(AST)会帮助我们理解在代码何处和代码怎样被转化。

模块化思维的好处

没有开发者会从随意的开始一个工程。许多从一个拥有变量、mixin、函数和常规的公用组件的 SASS 样板开始。我们会为变量、mixin、函数和常规的公用组件分别构建样式表,从而使我们的生产变得更加容易。在这一天的末尾,我们以 10 个或者更多的样式表来保持代码的组织性。 维护一个不断增加代码片段的 Sass 或是 LESS 库是一项不可避免的工作,同时也会让项目变得十分臃肿。许多包含了”以防万一”代码的项目拥有很多没有用到的 mixin 和函数。PostCss 为我们提供了易安装的即插即用(plug-and-play)的插件模块,使得为项目特殊需要的开发过程更加灵活。 PostCSS 用我们所有生产环境的样式表来迁移所有需要用到的代码去生成函数、公用组件和 mixin 并且将他们包装成插件。现在,对每一个项目来说,我们可以通过在我们的构建工具引入插件来选取需要的工具。 PostCSS FontPath 插件就是展现 PostCSS 神奇能力的一个例子。我们可以在允许用户使用的 webfonts 文件中用 Sass 引入一个 mixin。因此我们写入了@font-face 标签。

@mixin importfont($font-family, $font-filename, $font-weight : normal, $font-style :normal, $font-stretch : normal) {
  @font-face {
    font-family: '#{$font-family}';
    src: url('#{$font-filename}.eot');
    src: url('#{$font-filename}.woff') format('woff'),
    url('#{$font-filename}.ttf') format('truetype');
    font-weight: $font-weight;
    font-style: $font-style;
    font-stretch: $font-stretch;
  }
}
@include importfont('mission script', 'fonts/mission-script-webfont', 300);

如果在我们的项目中使用 PostCSS FontPath 插件,我们就不再需要像上面那个例子一样引用 Sass mixins。我们可以在我们的 CSS 中写入如下代码,PostCSS 会通过 Grunt 或者 Gulp 来将它转化为我们需要的代码。

@font-face {
  font-family: 'My Font';
  font-path: '/path/to/font/file';
  font-weight: normal;
  font-style: normal;
}

截至到这篇文章发表时,社区中已经有超过 100 个目前可用的插件,它们允许我们使用未来的 CSS 语法、缩写、工具和这门语言的拓展。它不仅仅是一个“很酷的工具”,而且在它的用户基数中正计入 WordPress、Google 和 Twittter 的团队。

添加 PostCSS 到你的工作流中

由于 PostCSS 是基于 Javascript 的,我们在项目中可以使用像 Gulp 和 Grunt 这样的构建工具去转化 CSS。下面的教程演示了如何通过 Gulp 或 Grunt 在你的工作流中添加 PostCSS。使用哪种构建工具并不重要,这仅仅取决于个人的偏向或哪种对于我们的项目来说是最好的。

note: 在 Github 可以找到可用于 Gulp 和 Grunt 这两者的完整版。可以将它作为一个初学者的模板随意使用,并且可以按需拓展。

开始在 Gulp 中使用 PostCSS

如果你不熟悉 Gulp,推荐你阅读 Callum Macrae 写的”building-with-gulp”来开始使用这个构建工具。 在终端运行以下命令来安装 PostCSS 模块至你的项目:

npm i gulp-postcss -D

在你项目的 Gulpfile.js 中,我们需要去引入安装好的 PostCSS 模块然后在一个任务中使用它。确认更新你的开发文件路径和转化过的输出文件的目录。

var postcss = require('gulp-postcss');

gulp.task('styles', function () {
    return gulp.src('path/to/dev/style.css')
        .pipe(postcss([]))
        .pipe(gulp.dest(path/to/prod))
});

在命令行输入gulp styles来运行这个任务。

开始在 Grunt 中使用 PostCSS

如果你不熟悉 Gulp,推荐你阅读 Mike Cunsolo 写的”Get Up and Running With Grunt”来开始使用这个构建工具。 在终端中运行以下命令来在你的项目中安装 PostCSS 模块:

npm i grunt-postcss -D

当插件在你的系统中安装完成后,像下面那样将它在 Gruntfile 中引入并且创建一个任务。确认以你的项目目录结构更新了你的 cwd 和 dest 目录值。

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    styles: {
      options: {
        processors: []
      },
      dist: {
        files: [{
          expand: true,
          cwd: 'dev/',
          src: ['**/*.css'],
          dest: 'prod/'
        }]
      }
    }
  });

  // Load post-css.
  grunt.loadNpmTasks('grunt-postcss');

};

在终端中输入grunt styles来执行这个任务。

安装插件

仅仅使用 PostCSS 本身并不能体现它的强大之处。它之所以强大是依靠它的插件。你可能已经注意到了在上面的 Gulp 和 Grunt 实现中任务声明是空数组。这些数组值就是我们想要在我们工具中引入的特性,也是我们能导入在社区成长起来的 PostCSS 插件的地方。 已获支持的 PostCSS 插件列表可以在PostCSS’ GitHub page找到。像所有的 NPM 包一样,插件可以通过命令行安装。许多插件只能用于 PostCSS 的拓展并且对于你正使用的构建工具是未知的。举个例子,我们安装一个PostCSS Focus插件,用来给每个鼠标悬浮状态添加一个:focus。 下面所有的例子中的插件,我们都需要用命令行和 NPM 来在我们的项目中安装插件包。

PostCSS 插件安装示例

npm i postcss-focus -D

插件可以直接传递进之前写好的方法中。但是为了代码简洁,我们可以构造一个数组并把它当做一个参数传递。在这个数组中,我们可以引入必要的require声明,从而返回插件的值并立即调用。如果你想要对这个概念了解更多,浏览一下 Ryan Christiani 所写的Functions as First-Class Citizens in JavaScript

require('postcss-focus')()

以下是为 Grunt 改进过的代码,使用了我们最新构造的processorArray

var processorArray = [
    require('postcss-plugin')()
];

// Snipped for brevity
styles: {
      options: {
        processors: processorArray
      },
      dist: {
        src: 'path/to/dev/style.css',
        dest: 'path/to/prod/style.css'
      }
    }

这里是为 Gulp 改进过的代码:

var processorArray = [
    require('postcss-plugin')()
];

gulp.task('styles', function () {
    gulp.src('path/to/dev/style.css')
        .pipe(postcss(processorArray))
        .pipe(gulp.dest('path/to/prod'))
});

插件

当我们已经安装好了一个插件并且我们的构建工具准备好去编译我们的代码时,我们可以用 PostCSS 和它插件的特色。首要任务是在保存我们开发代码的目录下创建一个带有扩展名为.css的文件。 “等一下!你说一个 CSS 文件吗?”对哒,一个 CSS 文件。因为 PostCSS 转换的是 CSS,我们不需要去用一个特殊的语法,传统的 CSS 就可以。如果你熟悉预处理器,那么对你来说将.sass.scss.stylor.less文件抛在一遍并且回到.css的怀抱是很不自然的。但是,事实上,这不是改变他的本质,只是形式上的转化。 我们可以分别使用grunt stylesgulp styles的命令来运行我们的构建工具,从而使用我们新安装的 PostCSS 去处理我们的样式表。我们开发环境中的 CSS 文件会通过 PostCSS 插件和其它提供的插件进行处理,然后这些 CSS 文件会被输出到我们指定的生产目录中。 底下是一些可能会在你开始使用 PostCSS 时有所帮助的插件小贴士。包括了插件安装说明和使用说明。

自动添加前缀

在整个广袤无垠的浏览器和设备圈子里写可以兼容样式非常令人痛苦,并且保持更新需要浏览器前缀的属性和值本身也是一个挑战。幸运的是,Autoprefixer 可以推算出在哪并且在什么情况下需要去添加浏览器前缀。插件可以关注到属性的浏览器的前缀和需要前缀的值,它将我们解放,可以让我们在写样式时只需要写出在脑子里的现代特性和属性。 这是我们通过命令行安装插件的方式:

npm i autoprefixer -D

当我们将这个插件添加到之前构造好的数组中,我们可以提供一个包含了项目需要支持的浏览器范围的对象。配置项在Browserslist Github Account中提供。 让我们把 Autoprefixer 插件添加到我们的处理器中:

var processorsArray = [
  // snipped for brevity
  require('autoprefixer')({ browsers: ['last 2 versions', 'ie 6-8', 'Firefox > 20']  })
];

根据我们提供的浏览器范围,合适的浏览器前缀会被输出到我们样式中需要的所有 CSS 属性和值中。 这是开发环境的 CSS:

.item {
  display: flex;
  flex-flow: row wrap;
}

这是转化后输出的 CSS:

.item {
  display: -webkit-flex;
  display: -ms-flexbox;
  display: -webkit-box;
  display: flex;
  -webkit-flex-flow: row wrap;
      -ms-flex-flow: row wrap;
          flex-flow: row wrap;
}

CSSNEXT-使用未来的语法

CSS4 在不久就会出现在我们身边,伴随它而来的是原生变量、定制媒体查询、定制选择器和新的链接地址伪类。截至我在写这篇文章的时候,所有的浏览器都不支持这些新特性,当新的规范得到批准后它们就会在现代浏览器中得到应用。 CSSNext 是一个转化所有 CSS4 特点和属性至浏览器可以理解的 CSS3 的工具。这个工具可以独立使用也可以作为 PostCSS 的一个插件使用。再一次,我们可以可以将它添加到包含了我们其他 PostCSS 插件的processorsArray数组中。 通过命令行安装 CSSNext:

npm i cssnext -D

然后将插件添加到处理器中

var processorsArray = [
  // snipped for brevity
  require('cssnext')()
];

现在,在你的生产环境中可以写带有 CSS4 特性的代码了,PostCSS 会通过构建工具将你的语法转化,从而支持现在的浏览器。当浏览器支持更新的语法时,你可以将生产环境的 CSS 替换为转化过的 CSS。 这是生产环境的 CSS:

// Custom Variables
:root {
  --linkColour: purple;
}

a {
  color: var(--linkColour);
}

// Custom media queries with ranges
@custom-media --only-medium-screen (width >= 500px) and (width <= 1200px);

@media (--only-medium-screen) {
  /* Medium viewport styles */
}

// Custom selectors
@custom-selector :--enter :hover, :focus;
@custom-selector :--input input, .input, .inputField;

a:--enter {
  /* Enter styles go here */
}

:--input {
  /* Input field styles go here */
}

这是转换过的输出:

a {
  color: purple;
}

@media (min-width:500px) and (max-width:1200px){
  body{
    background:#00f;
  }
}

a:focus,a:hover{
  background:#00f;
}

.input, .inputField, input{
  background:red;
}

如果你想体验更多的 CSSNext 特性,这个网站有一个用 CSSNext 支持的 CSS4 特性供你消遣的场地。

Sass 语法

如果你的预处理器语言是 Sass,不要怕:你可以用 PostCSS 来使用它的语法和它的工具。尽管传统的 CSS 暂时还不支持变量、嵌套和引入外部文件,像 PreCSS 这样的插件能够让我们使用这些特点并且在我们传统的 CSS 文件中使用 Sass 语法。 如果我们将插件通过命令行添加到我们的构建工具中并在我们构造的数组中引入,我们就可以立即开始输入 Sass 的语法了。如果你想从 Sass 转投到 PostCSS 的怀抱中,你只需要把文件拓展名改为.css并且立即将它放入你构建工具的工作流中。 通过命令行安装 PreCSS:

npm i precss -D

在处理器中添加你的插件:

var processorsArray = [
  // snipped for brevity
  require('precss')()
];

这里是开发环境的 CSS:

/*Variables*/
$blue: #0000ff;
$green: #00ff00;
$radius: 10px;

.square {
  background: $blue;
  border-radius: $radius;
}

/*Imports*/
@import "partials/_base.css";

/*Mixins*/
@define-mixin square $dimension {
    width: $dimension;
    height: $dimension;
}

/*Nesting, variables and mixins*/
.button {
  @mixin square 200px;
  background: $green;
  &:hover {
    background: $blue;
  }
}

这是输出后的 CSS:

.square {
  background: #0000ff;
  border-radius: 10px;
}

.button {
  width: 200px;
  height: 200px;
  background: #00ff00;
}

.button:hover {
  background: #0000ff;
}

使用社区中的插件拓展你的 CSS

尽管可用的插件能够帮助我们更有效率的去编写代码,PostCSS 的力量存在于社区中的插件。这些插件给我们提供了编写代码更为快速的途径,或者说至少是更简单的去实现有创造力的样式编写方式。使用 PostCSS 提供的 API,这些插件允许我们在项目中定制属性、选择器和属性值,这让我们更有效的编写样式同时也会减少 Google 的使用。

数量查询

数量查询是非常强大的功能。它允许我们用 CSS 给元素计数并且基于兄弟节点的数量来应用样式。它们相对你现在用到的 CSS 来说使用一些更先进的 CSS 选择器,所以这些选择器写出来有点小花招的感觉。 尽管有像 QQ 这样的在线工具能帮我们去写这些查询,我们也可以运用 PostCSS 直接在我们的样式中使用定制的选择器。 为了使用数量选择器,通过命令行在你的项目中安装数量选择插件像其他所有的插件一样:

npm i postcss-quantity-queries -D

添加插件到处理器中:

var processors = [
  // Snipped for brevity
  require('postcss-quantity-queries')()
];

当插件安装完成后,你就可以使用定制的选择器来基于匹配到的元素来应用样式,但仅仅只能通过使用插件来支持。 这是开发环境的 CSS:

// To apply styles if 'at least' number of sibling elements present
.container > .item:at-least(5) {
  background: blue;
}

// To apply styles if 'at most' number of sibling elements present
.item > a:at-most(10) {
  color: green;
}

// To apply styles if number of sibling items 'between' a range is present
.gallery > img:between(4, 7) {
  width: 25%;
}

// To apply styles if 'exactly' number of provided items is present
.profiles:exactly(4) {
  flex: 1 0 50%;
}

这是转化输出后的 CSS:

// To apply styles if 'at least' number of sibling elements present
.container > .item:nth-last-child(n+5), .container > .item:nth-last-child(n+5) ~ .item {
  background: blue;
}

// To apply styles if 'at most' number of sibling elements present
.item > a:nth-last-child(-n+10):first-child, .item > a:nth-last-child(-n+10):first-child ~ a {
  color: green;
}

// To apply styles if number of sibling items 'between' a range is present
.gallery > img:nth-last-child(n+4):nth-last-child(-n+7):first-child, .gallery > img:nth-last-child(n+4):nth-last-child(-n+7):first-child ~ img {
  width: 25%;
}

// To apply styles if 'exactly' number of provided items is present
.profiles:nth-last-child(4):first-child, .profiles:nth-last-child(4):first-child ~ .profiles {
      flex: 1 0 50%;
}

使用 short 插件来拓展简写属性

当你在写样式的时候,你可能有这样的遭遇,遇到一个属性的语法你会说:“这可以更简短一些的。”幸运的是,Short 插件帮助我们做了这样一件事:更有逻辑的编写样式。它让我们可以用简写的属性来写position和’size’,更像backgroundfont被压缩成一个声明的方式。 简写的声明会通过 PostCSS 的 API 被转换为浏览器可接受的样式,能使样式表在开发时更加简洁和有组织性。 通过命令行安装 Short;

npm i postcss-short -D

添加到你的处理器中:

var processors = [
  require('postcss-short')()
];

text属性包含了这些用于排版的属性:color, font-style, font-variant, font-weight, font-stretch, text-decoration, text-align, text-rendering, text-transform, white-space, font-size, line-height, letter-spacing, word-spacing and font-family. 这是开发环境的 CSS:

p {
  text: 300 uppercase dimgrey center 1.6rem 1.7 .05em;
}

这是转化后输出的 CSS:

p {
  color: dimgrey;
  font-weight: 300;
  text-align: center;
  text-transform: uppercase;
  font-size: 25px;
  font-size: 1.6rem;
  line-height: 1.7;7
  letter-spacing: .05em;
}

position属性允许在一个声明中包含top,left,right,bottom。这些值的顺序是按顺时针方向。如果有值不需要填写,只需要在该属性值的位置上写一个*即可。 这是开发环境的 css:

section {
  position: absolute 10px * *;
}

.topNav {
  position: fixed 0 * * 0;
}

.modal {
  position: fixed 40% 25%;
}

这是转化后输出的 css:

section {
  position: absolute;
  top: 10px;
}

.topNav {
  position: fixed;
  top: 0;
  left: 0;
}

.modal {
  position: fixed;
  top: 40%;
  right: 25%;
  bottom: 40%;
  left: 25%;
}

这对我们的生产意味着什么?

而今使用 PostCSS 并且完全可以说出它的好处。你可以通过在你的构建工具中处理 PostCSS 来将其包含在你的工作流中,这更像是我们如何编译 Sass 和 LESS。引入一个像 PreCSS 的插件能够让你写好的 Sass 项目可以在不需要任何语法转变的情况下投入 PostCSS 的怀抱。 自我写这篇文章起,一个关于哪里是编写 CSS 的最好地方的讨论正在进行。当我们使用像 React 这样日渐流行的框架,组件化编写样式的想法势不可挡,它让我们直接使用 javascript 来转化样式。尽管这些现在更多的是一个讨论,这也确实是一个用 PostCSS 来转换样式的方法。 另外一个在生产中兴风作浪的项目是 CSS 模块化,它将样式范围拓展至本地文件并且可以按需加载。这个概念已经在 javascript 的圈子里很流行了。当前端语言诸如 React 和 JSX 的界限持续变模糊,忽视 CSS 和 javascript 联合的力量是很难的。 尽管 PostCSS 通过定制语法和属性这样全新的方式来拓展 CSS,它对于想要顺畅的使用这种语言和它错综复杂的地方的初学者可能是一个挑战。如果你和一个新手开发者在一个项目中使用 PostCSS,尝试去鼓励他们去深层次的理解这个语言并且理解 PostCSS 这样一个和 Sass 非常相像的生产工具是怎样让我们的样式更有效率的编写。

拥抱 PostCSS

在将来的几年内,我们使用 CSS 的方式会有许多不同的方式。每个项目我们都会有不同的依赖,我们需要这些依赖去适配我们的生产方式。在一个像 PostCSS 这样模块化的生态系统中工作让我们可以选择我们想要完成项目所要用到的特性。 我更乐意你去探索 PostCSS 的世界和所有已被支持的插件。因为这是一个社区项目,这个生态系统只能在人们使用它并且创造插件中成长。为了去探索更多的插件,可以查看 NPM 上的可用包,也可以在PostCSS Playground上测试插件的功能。