postcss开发实战

来源: Qunar技术沙龙 作者: 邵裕东 | 发布时间: 2019-03-08 09:30:00

css是web开发中重要的一部分,然而css规范仍存在一些浏览器兼容性问题,由此出现了sass、less、stylus等css预处理器,提高了开发人员的效率。

来呀!来呀!关注我吧!!

邵裕东,2018年3月入职Qunar,现任平台事业部大前端技术中心前端开发工程师,负责公司移动端框架hy、qrn的开发维护,nanachi小程序多端转译框架开发。追求前端工程化,喜欢做一切有意义的事。


初识 postcss

css 是 web 开发中重要的一部分,然而 css 规范仍存在一些浏览器兼容性问题,由此出现了 sass、less、stylus 等 css 预处理器,提高了开发人员的效率。 postcss 的官方定义: A tool for transforming CSS with JavaScript. 一个用 javascript 来处理 css 语法的工具。postcss 本身不会对 css 文件进行修改,它只将 css 文件转化为抽象语法树(abstract syntax tree,后简称 ast),然后插件对语法树进行处理,最后由 postcss 将 ast 还原为普通 css,所以 postcss 对 css 文件的修改都是基于插件来实现的。

postcss 可以做什么

截止到目前,postcss 已有200多个插件,列举一些比较出名的插件: 1、autoprefixer 它从 caniuse 网站上的数据,自动添加浏览器前缀到 css 中,帮助开发人员解决浏览器兼容问题。 2、postcss-preset-env 支持 css 的最新特性,并兼容大多数浏览器。 3、postcss-modules 模块化 css 代码,为选择器提供命名空间后缀。 4、precss 解析一些类 sass 语法,包括:变量、嵌套、mixins等。 5、stylelint css 语法检查器,可以帮助开发者检查 css 文件中的语法错误。 更多插件地址:https://github.com/postcss/postcss/blob/master/docs/plugins.md

postcss 用法

postcss 可以与各种前端打包工具相结合使用,例如 webpack、gulp、grunt、rollup...

webpack

  1. // webpack.config.js

  2. module.exports = {

  3. module: {

  4. rules: [

  5. {

  6. test: /\.css$/,

  7. exclude: /node_modules/,

  8. use: [

  9. {

  10. loader: 'style-loader',

  11. },

  12. {

  13. loader: 'css-loader',

  14. options: {

  15. importLoaders: 1,

  16. }

  17. },

  18. {

  19. loader: 'postcss-loader'

  20. }

  21. ]

  22. }

  23. ]

  24. }

  25. }

  26. // postcss.config.js

  27. module.exports = {

  28. plugins: [

  29. require('precss'),

  30. require('autoprefixer')

  31. ]

  32. }

也可以直接使用 postcss 提供的 js api 直接处理 css。

JS API

  1. const autoprefixer = require('autoprefixer')

  2. const postcss = require('postcss')

  3. const precss = require('precss')

  4. const css = `

  5. .test {

  6. color: #fff;

  7. }

  8. `;


  9. postcss([precss, autoprefixer])

  10. .process(css, { from: 'src/app.css', to: 'dest/app.css' })

  11. .then(result => {

  12. console.log(result.css);

  13. });

从零开始开发一个 postcss 插件

抽象语法树

开发 postcss 插件前,首先要理解的一个概念就是文章开头提到的抽象语法树,postcss 将 css 文本转化为 ast,javascript 可以通过一系列 api 来操作、改变 ast,再由解析器将 ast 解析成普通目标 css 文本。 css 文件:

  1. body {

  2. margin: 0;

  3. }

转化为 ast,对应的 json:

  1. {

  2. "raws": {

  3. "semicolon": false,

  4. "after": ""

  5. },

  6. "type": "root",

  7. "nodes": [

  8. {

  9. "raws": {

  10. "before": "",

  11. "between": " ",

  12. "semicolon": true,

  13. "after": "\n"

  14. },

  15. "type": "rule",

  16. "nodes": [

  17. {

  18. "raws": {

  19. "before": "\n\t",

  20. "between": ": "

  21. },

  22. "type": "decl",

  23. "source": {

  24. "start": {

  25. "line": 2,

  26. "column": 2

  27. },

  28. "input": {

  29. "css": "body {\n\tmargin: 0;\n}",

  30. "id": "<input css 32>"

  31. },

  32. "end": {

  33. "line": 2,

  34. "column": 11

  35. }

  36. },

  37. "prop": "margin",

  38. "value": "0"

  39. }

  40. ],

  41. "source": {

  42. "start": {

  43. "line": 1,

  44. "column": 1

  45. },

  46. "input": {

  47. "css": "body {\n\tmargin: 0;\n}",

  48. "id": "<input css 32>"

  49. },

  50. "end": {

  51. "line": 3,

  52. "column": 1

  53. }

  54. },

  55. "selector": "body"

  56. }

  57. ],

  58. "source": {

  59. "input": {

  60. "css": "body {\n\tmargin: 0;\n}",

  61. "id": "<input css 32>"

  62. },

  63. "start": {

  64. "line": 1,

  65. "column": 1

  66. }

  67. }

  68. }

其中,ast 的第一级是一个 root 类型的节点,type 除 root 外还包括 rule、decl、atrule、comment,nodes 属性表示节点下的子节点。 postcss 为我们提供了一些基本的 ast 操作方法: -walk:遍历所有节点信息 -walkAtRules:遍历所有 atrule 类型节点 -walkRules:遍历所有 rule 类型节点 -walkComments:遍历所有 comment 类型节点 更多节点信息参照 postcss api 文档:https://api.postcss.org/。

插件开发

我们开发一个将像素值乘以2的插件。

  1. const postcss = require('postcss');


  2. const postcssPluginParseMargin = postcss.plugin('postcss-parse-margin', function(){

  3. return function(root){

  4. root.walkDecls(function(decl){

  5. decl.value = decl.value.replace(/(\d*\.?\d+)\s*px/, function(match, numStr) {

  6. return Number(numStr) * 2 + 'px';

  7. });

  8. });

  9. }

  10. });

  11. const css = `

  12. .test {

  13. font-size: 2.5px;

  14. }

  15. `;

  16. postcss([postcssPluginParseMargin]).process(css).then(function(res){

  17. console.log(res.css);

  18. });

  19. /**

  20. .test {

  21. font-size: 5px;

  22. }

  23. */

我们调用 walkDecls 函数,从 root 节点开始遍历所有的 decl 类型节点,取出 decl.value 中的像素值,乘以2后替换掉原有 decl.value,这样就实现了一个简单的 postcss 插件。

实际问题:postcss 转义 less 语法

less 是一种 css 预处理器,为 css 扩展了更多的功能,如变量、继承、mixins、函数等。如果我们想基于 less 的语法使用 postcss 的插件,一种方案是使用 less 将 less 语法转义成css 语法后,再用 postcss 处理转义后的 css 文件,但这种方案由于要经历两次语法树的生成过程,比较耗时。与 sass 不同,less 本身就是 js 编写的,因此我们可以基于 less 自带的语法树解析器,将 less 语法树直接转化为 postcss 语法树,就可以无缝衔接使用其他 postcss 插件。 这个轮子已经有大神帮我们造好,叫做 postcss-less-engine,只是项目已有一年多未更新,less 还是基于2.7.1版本,更新到3.0.0以上会报错。查阅了 less 的 changelog,发现原因是3.0.0以后,less 解析器中的 Directive 类重命名为 AtRule,Rule 重命名为 Declaration。bug 修复好已提 pr 等待作者更新,大家如有需要支持最新 less 可以暂时使用 postcss-less-engine-latest,git 地址:https://github.com/Crunch/postcss-less。 插件用法:

  1. const postcss = require('postcss');

  2. const postCssLessEngine = require('postcss-less-engine');

  3. const css = `

  4. @color: red;

  5. .test {

  6. color: @color;

  7. }

  8. `;


  9. postcss([

  10. postCssLessEngine()

  11. ]).process(

  12. css, {

  13. parser: postCssLessEngine.parser

  14. }

  15. ).then((result)=>{

  16. console.log(result.css);

  17. /**

  18. .test {

  19. color: red;

  20. }

  21. */

  22. });

如果只想支持 less 的部分特性,完全可以自己编写 postcss 插件来支持,postcss-less 插件可以用来识别 less 语法,为 postcss 抽象语法树添加 less 标识,但是不会转换原有代码,转换工作交给其他 postcss 插件来完成,大家有兴趣可以开发适合自己需求的插件。

公众号导航