PostCSS自学笔记(二)【插件篇】

PostCSS经常使用插件介绍

继上一次PostCSS学习指南(一)后,渐渐开始在项目中应用。javascript

此次决定主要讲解一些我的认为很是有帮助的PostCSS插件。css

本期主要介绍如下几个插件和几个坑java

  1. autoprefixer
  2. postcss-partial-import
  3. postcss-advanced-variables
  4. cssnano
  5. postcss-px2rem
  6. precss
  7. postcss-nestingpostcss-nested
  8. 坑( ఠൠఠ )ノ

autoprefixer

这个就不用多说了,必装插件之一。方便的写规范的css,它会为你提供很是完整的hack兼容方案的。固然这里须要了解一下的是,它的大部分兼容数据来源Can I Use,另一个稍微须要了解的插件配置参数就是browsers,好比这样写:node

module.exports = {
  plugins: [
    require('autoprefixer')({ browsers: 'last 2 versions' })
  ]
}

关于这个参数的详细介绍能够看看Browserslist Queries,文中说了,强烈建议将查询写入package.json(后面会告诉你为什么要写在这里),而非配置postcss.config.js中autoprefixer的browsers参数。因此此处建议写法以下:webpack

postcss.config.jsgit

module.exports = {
  plugins: [
    require('autoprefixer');
  ]
}

package.json内增长以下示例github

"browserslist": [
  "> 1%",
  "last 2 versions"
]

这里我对着官方文档简单说一下数组内的值对应的含义:web

  • last 2 versions: 每一个浏览器中最新的两个版本。
  • > 5% or >= 5%: 全球浏览器使用率大于5%或大于等于5%(上例中则是1%)。

其余的一些参数简单介绍:json

  • ie 6-8: 选择包含ie6-8的版本。
  • Firefox > 20: 火狐版本号大于20。

还有不少不一一列举,这里的配置仍是很详细的,通常来讲最省事的就是不加参数,按照默认便可。segmentfault

须要配置的话,就在package.json里面添加browserslist参数,这样其余插件也可以从中获取到项目将要兼容的版本,目前包含如下几个插件会读取改配置:

关于autoprefixer的介绍差很少就这些了~

postcss-partial-import

这个没啥好说的,也是很容易理解的插件,就是让你的css文件支持@import,支持W3C的写法也支持SASS那种写法,这里就很少说啦。

postcss-advanced-variables

一样的,像SASS那样能够自定义变量并进行引用,用法也十分简单,相信你们必定不用点开官方文档也会用的~(你确定仍是点开了哈哈哈~你这叫胡开连接综合症,生怕错过了啥内容。)

cssnano

很显然这个插件做者比较高调,github的cssnano上面是没有什么说明和介绍的,固然官方也写得很详细了。这个插件给个人感受就是css代码压缩工具(实际上它集成了不少强化的功能,表面上看起来是压缩实际上进行了至关多处理,能够看看这个表格Optimisations),他的配置建议采用默认配置,除非你知道你在作什么。

固然我在测试使用中遇到了一点点问题,关于cssnano和autoprefixer结合使用,或者说是postcss.config.js插件引入顺序有关的形成输出不一样的问题?我暂时还在研究,先看代码:

testcssnano.css

.test {
  -moz-border-radius: 10px;
  border-radius: 10px;
  display: flex;
}

postcss.config.js(第一种)

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

输出结果:

.test{border-radius:10px;display:flex}

postcss.config.js(第二种)

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

输出结果:

.test{border-radius:10px;display:-webkit-box;display:-ms-flexbox;display:flex}

问题一,若是手写-moz-,cssnano会清除,可见示例,固然或许这个属性在火狐已经支持不需前缀因此,就被去掉了。

问题二,若是在配置文件中,cssnano在autoprefixer以前引用,假设根据webpack的loader执行顺序规则相同的话,大概postcss.config.js中的插件也是这样由下而上依次执行,所以,在第一种例子中,css代码先被autoprefixer处理,而后再执行cssnano清除了那些多余的前缀代码?大体可分解为:

第一步autoprefixer处理结果:

.test {
  -moz-border-radius: 10px;
  border-radius: 10px;
  display: flex;
  display: -webkit-box;
  display: -ms-flexbox;
}

第二步cssnano处理结果:

.test{border-radius:10px;display:flex}

但是我要遗憾的告诉你们。。。这个多是错误的结论!!!

由于我将第一步的结果做为初始css,将postcss.config.js中仅引用一个cssnano插件来执行的结果以下:

.test{border-radius:10px;display:flex;display:-webkit-box;display:-ms-flexbox}

所以,这个问题就来了。。。cssnano确实将-moz-去掉,可是他并无处理其余的关于display的兼容代码。

因而我再作了一个测试:

我将css代码改成:

.test {
  -webkit-transform: scale(.5) translate(10, 20);
  -moz-transform: scale(.5) translate(10, 20);
  -ms-transform: scale(.5) translate(10, 20);
  -o-transform: scale(.5) translate(10, 20);
  transform: scale(.5) translate(10, 20);
}

而postcss.config.js仍是同时引入cssnano和autoprefixer,通过测试,不管谁先谁后输出结果均为:

.test{-webkit-transform:scale(.5) translate(10,20);transform:scale(.5) translate(10,20)}

若是css代码改成:

.test {
  transform: scale(.5) translate(10, 20);
}

postcss.config.js同时引入cssnano和autoprefixer,

第一种cssnano在前结果:

.test{transform:scale(.5) translate(10,20)}

第二种autoprefixer在前结果:

.test{-webkit-transform:scale(.5) translate(10,20);transform:scale(.5) translate(10,20)}

结果又不同,不知道看到这里你们晕了没有。

根据观察,postcss.config.js的插件执行顺序是有要求的,至少在同时使用cssnano和autoprefixer的时候,cssnano必定要在autoprefixer以后,不然autoprefixer可能会失效!

另外cssnano和autoprefixer都会对很旧的兼容写法进行精简,例如上文有一段有不少个transform属性的css代码,autoprefixer会(根据browserslist)剔除其余的。

至于顺序问题,我后面继续进行研究!

postcss-px2rem

作移动端,适配是个头疼的问题,不过我目前仍是使用想对稳定的方案flexible,那么就须要用rem来作主要的单位,这里不说适配问题只说这个插件,通常移动端,设计师给的设计稿都是750px宽,你只须要下面这样设置,就能够直接在代码里面写你在PSD量的像素值了,这真是太使人激动了!

require('postcss-px2rem')({
  remUnit: 75,
  threeVersion: true
})

由于这个postcss-px2rem又是来源于px2rem的,因此详细的说明见px2rem,我这里写了两个参数,一个remUnit,这个对应的是每rem对应的px值,既然750px,就写75啦,不知道这样理解对不对。另外一个threeVersion则对应三个不一样dpr下的大小,这个比较少用,须要注意的是处理这些参数,是否转换rem都和注释有关,这里就不细说了,看看文档就好~固然这里也埋下一个坑,等下会提到。

precss

这就是个大杂烩,主要是为了知足SASS开发者的习惯,继承了不少插件,本篇先后文都有说起precss内的部分插件,若是并非所有用到,我建议仍是一个个手动安装所需插件来进行配置,这东西,配好了之后也就不会常常改动了,并且我的认为用大杂烩很容易出现一些坑,又很难排查,例以下面两个插件,你们仔细看看。

另外还有几个插件,建议安装(固然若是你彻底不知道干啥的能够忽略):

  1. postcss-mixins 一个和SASS的mixins用法相同的插件
  2. postcss-atroot 让你的嵌套css处于根部,官方有个bar.css的@import的例子很棒,能够看看触类旁通
  3. postcss-extend 有相同结构却有那么一点点不一样的区别,用这个能够方便的统一管理相同部分样式代码

其余的一些我我的以为不经常使用或者说实用意义不大因此就没有写出来了。

postcss-nestingpostcss-nested

这两个也是从precss里面拿出来的,就是仿SASS的嵌套css写法用。为啥把这两个放一块儿写,由于他们长的太像了。只看名字鬼知道他们的区别,然而他们都被加入到precss里面去了。据precss介绍,他们两个的区别是:

光看这两行我是看不懂究竟是个啥玩意,难道第一个是符合W3C规范的嵌套选择?粗略看了下两个插件的说明文档,没看出没啥区别。行,那手动写代码来一个个试试。先安装postcss-nesting,编译试试,哗嚓一片红。。。咋回事,咱们先看看代码片断。

.catis-list {
  padding: 0 50px;
  overflow: hidden;
  li {
    list-style: none;
    float: left;
    margin-top: 38px;
    width: 113px;
    &:not(:nth-child(4n)) {
      margin-right: 66px;
    }
    .iconfont {
      font-size: 100px;
      line-height: 100px;
      color: #506071;
      display: block;
      text-align: center;
    }
    .cati-name {
      font-size: 28px;
      line-height: 40px;
      display: block;
      color: #999;
      text-align: center;
    }
  }
}

看似SASS写法没有任何问题。但是它提示的报错信息让人看不大懂

ERROR in ./src/style/index.css
Module build failed: ModuleBuildError: Module build failed: Error: undefined:783:6: property missing ':'
    at error (D:\webProjects\mobileweb\node_modules\css\lib\parse\index.js:62:15)
    at declaration (D:\webProjects\mobileweb\node_modules\css\lib\parse\index.js:223:33)
    at declarations (D:\webProjects\mobileweb\node_modules\css\lib\parse\index.js:252:19)

啥玩意missing了个冒号嘛。。。改来改去都不对。索性拿来官方的实例。才看清楚。原来每一个嵌套的样式前面都须要一个&(注意符号后面有个空格),实际上应该以下才对:

.catis-list {
  padding: 0 50px;
  overflow: hidden;
  & li {
    list-style: none;
    float: left;
    margin-top: 38px;
    width: 113px;
    &:not(:nth-child(4n)) {
      margin-right: 66px;
    }
    & .iconfont {
      font-size: 100px;
      line-height: 100px;
      color: #506071;
      display: block;
      text-align: center;
    }
    & .cati-name {
      font-size: 28px;
      line-height: 40px;
      display: block;
      color: #999;
      text-align: center;
    }
  }
}

这里面有一段用到伪类其中&符号后面是没有空格的,是正确的。编译后的结果:

.catis-list {
  padding: 0 0.666667rem;
  overflow: hidden;
}

.catis-list li {
  list-style: none;
  float: left;
  margin-top: 0.506667rem;
  width: 1.506667rem;
}

.catis-list li:not(:nth-child(4n)) {
  margin-right: 0.88rem;
}

.catis-list li .iconfont {
  font-size: 1.333333rem;
  line-height: 1.333333rem;
  color: #506071;
  display: block;
  text-align: center;
}

.catis-list li .cati-name {
  font-size: 0.373333rem;
  line-height: 0.533333rem;
  display: block;
  color: #999;
  text-align: center;
}

那么这样就没有问题了。和官方的说明的是同样的,可是另外一方面,若是每次都要写&加空格,那岂不是很麻烦,习惯写SASS的兄弟们确定不肯意这样作啦。

那么我要说的就是另外一个插件postcss-nested,如precss所述,这个的确是SASS-LIKE了。固然我暂时还不太明白为什么precss要收入两个nest插件(为了知足不一样开发人员的习惯?)。咱们修改postcss.config.js使用postcss-nested,并从新修改样式代码以前第一段,再次编译执行,一切OK,那么结论就是,若是仅习惯于SASS的嵌套写法,安装postcss-nested插件便可~

坑( ఠൠఠ )ノ

  1. 最后来讲说我遇到的坑,除了刚才说的cssnano和autoprefixer同时使用须要注意顺序问题。还有另外一个顺序研究,就是肯定好将要使用的插件后,在postcss.config.js中配置插件require顺序仍是有讲究的,这里我我的观察的确仍是从上往下的(至因而不是每一个插件轮流处理完文件后挨个执行我尚不肯定),好比说,postcss-partial-import这个理应是第一个引入的,你如果把它放在最后面,而css代码中第一行就用了@import那确定会报错!因此,建议根据css代码的写法来决定你的执行顺序。
  2. postcss-nested插件是大部分SASS开发者所喜好的,可是你在css文件中用sass写法会遇到如下几个问题:

    1. csslint,css文件不支持嵌套,变量等写法,若是你将文件模式改成sass,注释的方式会变成//,而非/* comments */,固然你能够手写/* ... */这样的注释,可是用快捷键进行注释会很痛苦。

      这里有个小技巧,让项目全部css文件均为sass模式下编辑,在项目settings.json添加:

      "files.associations": {
        "*.css": "scss"
      }

      固然你如果想要支持//注释也是能够的,请再安装postcss-scss插件,我这里很少说这个了,由于我已经决定手写注释了?

    2. 你手写注释没有问题,然而编译出来的东西会出问题,你样式中最后一行若是将注释写在花括号内部,它转换出来的代码,注释会在外部,这是个大坑,由于在使用postcss-px2rem的时候,那注释来控制是否转换的功能就失效了。我文字描述可能让人迷糊,因此看看代码:

      例如CSS部分代码:

      .test {
        color:#999;
        border:1px solid #ddd; /* no */
        .inner {
          color:#333;
        }
      }

      转换后:

      .test {
        color:#999;
        border:0.13333rem solid #ddd;
      }
      /* no */
      .test .inner {
        color:#333;
      }

      而不是我想要的:

      .test {
        color:#999;
        border:1px solid #ddd;
      }
      .test .inner {
        color:#333;
      }

      理论上是应该输出我想要的结果,却没有输出正确,错误的将1px的border转成rem,缘由就是先执行的postcss-nested将注释弄在外部后,postcss-px2rem没法识别到它的规则了。

      我一开始也是装了precss后发现该问题,后来查了好久才发现是这个插件的问题。同时也发现了这我的其实已经提过issue了Wrong location of comment of the last declaration in a nested rule definition只是没人解决。

      而我临时处理的方法就是只好将要注释的那段代码不要写在最后一行了。以下:

      .test {
        border:1px solid #ddd; /* no */
        color:#999;
        .inner {
          color:#333;
        }
      }

      可能有时候并不会有两个样式给border垫底,若是只有一行border的样式,就只能这样:

      .test {
        .inner {
          color:#333;
        }
        border:1px solid #ddd; /* no */
      }

      这样却是能够了,不过看起来很奇怪,因此若是你使用和我相同的处理方式时,务必注意此点(写完本文时我彷佛发现了更好的解决方案,等确认了没有问题后,放在下一期来写~)。

(我记得好像还有坑的,硬是想了半天想不起来了。。。那就先这样吧)

本期结语

最后若是你还想探索更多好玩的好用的插件,你能够看看这里PostCSS Plugins List还有这个能够搜索的分类插件列表,若是你发现了更棒的插件,也欢迎留言喔~
另外上面的例子若是我没有写的很明白,或者你只是想直接拿来快速测试使用的话,能够看看这个mobileweb

(关于第三期PostCSS的内容还在考虑。。。多是对第二期的补充和填坑~敬请期待?)

其余

关于我我的的PostCSS一系列学习, 介绍及总结, 有兴趣能够参阅: