postcss開發實戰

shaoyudong發表於2019-01-15

初識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檔案中的語法錯誤。

更多外掛地址:github.com/postcss/pos…

postcss用法

postcss可以與各種前端打包工具相結合使用,例如webpack、gulp、grunt、rollup...

webpack

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
            }
          },
          {
            loader: 'postcss-loader'
          }
        ]
      }
    ]
  }
}
// postcss.config.js
module.exports = {
  plugins: [
    require('precss'),
    require('autoprefixer')
  ]
}
複製程式碼

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

JS API

const autoprefixer = require('autoprefixer')
const postcss = require('postcss')
const precss = require('precss')
const css = `
.test {
	color: #fff;
}
`;

postcss([precss, autoprefixer])
    .process(css, { from: 'src/app.css', to: 'dest/app.css' })
    .then(result => {
    	console.log(result.css);
	});
複製程式碼

從零開始開發一個postcss外掛

抽象語法樹

開發postcss外掛前,首先要理解的一個概念就是文章開頭提到的抽象語法樹,postcss將css文字轉化為ast,javascript可以通過一系列api來操作、改變ast,再由解析器將ast解析成普通目標css文字。

css檔案:

body {
    margin: 0;
}
複製程式碼

轉化為ast,對應的json:

{
  "raws": {
    "semicolon": false,
    "after": ""
  },
  "type": "root",
  "nodes": [
    {
      "raws": {
        "before": "",
        "between": " ",
        "semicolon": true,
        "after": "\n"
      },
      "type": "rule",
      "nodes": [
        {
          "raws": {
            "before": "\n\t",
            "between": ": "
          },
          "type": "decl",
          "source": {
            "start": {
              "line": 2,
              "column": 2
            },
            "input": {
              "css": "body {\n\tmargin: 0;\n}",
              "id": "<input css 32>"
            },
            "end": {
              "line": 2,
              "column": 11
            }
          },
          "prop": "margin",
          "value": "0"
        }
      ],
      "source": {
        "start": {
          "line": 1,
          "column": 1
        },
        "input": {
          "css": "body {\n\tmargin: 0;\n}",
          "id": "<input css 32>"
        },
        "end": {
          "line": 3,
          "column": 1
        }
      },
      "selector": "body"
    }
  ],
  "source": {
    "input": {
      "css": "body {\n\tmargin: 0;\n}",
      "id": "<input css 32>"
    },
    "start": {
      "line": 1,
      "column": 1
    }
  }
}
複製程式碼

其中,ast的第一級是一個root型別的節點,type除root外還包括rule、decl、atrule、comment,nodes屬性表示節點下的子節點。

postcss為我們提供了一些基本的ast操作方法:

  • walk:遍歷所有節點資訊
  • walkAtRules:遍歷所有atrule型別節點
  • walkRules:遍歷所有rule型別節點
  • walkComments:遍歷所有comment型別節點

更多節點資訊參照postcss api文件:api.postcss.org/

外掛開發

我們開發一個將畫素值乘以2的外掛。

const postcss = require('postcss');

const postcssPluginParseMargin = postcss.plugin('postcss-parse-margin', function(){
	return function(root){
		root.walkDecls(function(decl){
			decl.value = decl.value.replace(/(\d*\.?\d+)\s*px/, function(match, numStr) {
				return Number(numStr) * 2 + 'px';
			});
		});
	}
});
const css = `
	.test {
		font-size: 2.5px;
	}
`;
postcss([postcssPluginParseMargin]).process(css).then(function(res){
	console.log(res.css); 
});
/** 
.test {
	font-size: 5px;
}
*/

複製程式碼

我們呼叫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-engin,只是專案已有一年多未更新,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地址:github.com/Crunch/post…

外掛用法:

const postcss = require('postcss');
const postCssLessEngine = require('postcss-less-engine');
const css = `
@color: red;
.test {
	color: @color;
}
`;

postcss([
    postCssLessEngine()
]).process(
    css, {
        parser: postCssLessEngine.parser
    }
).then((result)=>{
    console.log(result.css);
    /**
        .test {
            color: red;
        }
    */
});

複製程式碼

如果只想支援less的部分特性,完全可以自己編寫postcss外掛來支援,postcss-less外掛可以用來識別less語法,為postcss抽象語法樹新增less標識,但是不會轉換原有程式碼,轉換工作交給其他postcss外掛來完成,大家有興趣可以開發適合自己需求的外掛。

git地址:github.com/shellscape/…

相關文章