AndreGeng/AndreGeng.github.io

Postcss自定义插件

AndreGeng opened this issue · 1 comments

PostCSS是一个利用plugin来transform样式的工具.
PostCSS提供api, 用来分析/修改css文件的rule. plugin可以利用这个api来做很多有用的任务.

Architecture

总览

PostCSS和Sass/Less不一样它不是一种样式语言, 而是一个用来做css语法转换的工具.

工作流

image
实现parser常见的有以下两种方式

  1. css source string -> AST
    代码量比较大的情况下性能比较差.
  2. 分成词法分析/解析两步(css sourcee string -> tokens -> AST)
    PostCSS采用的是这种方式. csstreebabel-parser也是采用的这种方式. 它的优点在于性能以及代码复杂度的降低.

核心结构

Writing a PostCSS Plugin

First PostCSS Plugin

postcss官方提供了一个boilterplate, 我们可以通过它来创建一个postcss插件.

git clone https://github.com/postcss/postcss-plugin-boilerplate.git
node ./postcss-plugin-boilerplate/start  // 运行start脚本来生成插件

start脚本会提问如下问题

    AUTHOR_NAME:  'Your name: ',
    AUTHOR_EMAIL: 'Your email: ',
    GITHUB_NAME:  'GitHub username: ',
    PLUGIN_NAME:  'Plugin name: postcss-',
    PLUGIN_DESC:  '\nFinish sentence with plugin description:\nPostCSS plugin ',
    KEYWORDS:     '\nFinish plugin keywords:\npostcss, css, postcss-plugin, '

并根据我们的回答生成相应的插件, 看下生成插件的index.js文件

var postcss = require('postcss');

module.exports = postcss.plugin('PLUGIN_NAME', function (opts) {
    opts = opts || {};

    // Work with options here

    return function (root, result) {

        // Transform CSS AST here

    };
});

root就是postcss提供的ast树的根节点, 它提供了一些方法方便我们遍历css ast树. 具体api可以在postcss api查看, 比较常用的方法就是像下面这样

    root.walkRules(function (rule) { // rule代表一个个css 声明块, e.g. ul {}
      rule.walkDecls(/^background(?:-image)?$/, (decl) => { // decl代表, 声明块里一条条属性键值, e.g. margin: 10px
      });
    });

上面代码做的就是找到所有包含background或是background-image的css属性, 键值. 然后我们可以通过改变decl.value来进行我们需要的转化,

      rule.walkDecls(/^background(?:-image)?$/, (decl) => {
        const p = (async function () {
          const match = b64Regex.exec(decl.value);
          try {
            const base64 = await convertFileToBase64(match[1].trim(), opts.basedir, opts.mime);
            decl.value = decl.value.replace(opts.b64Regex, base64);
          } catch (e) {
            console.error(e);
          }
        })();

注意: 如果我们的转换过程是异步的, 我们需要在transform ast tree的function里, 返回一个promise对象

module.exports = postcss.plugin('postcss-base64', function (options) {
  // Work with options here

  return function (root) {

    const promises = [];
    // Transform CSS AST here
    root.walkRules(function (rule) {
      rule.walkDecls(/^background(?:-image)?$/, (decl) => {
        const p = (async function () {
        })();
        promises.push(p);
      });
    });
    return Promise.all(promises);
  };
});

大概过程就是这样可以动手试下, 印象会比较深刻些..

其它工具:
postcss ast explorer