最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 从源码聊 postcss 和 babel 的 api 风格的不同

    正文概述 掘金(zxg_神说要有光)   2021-05-21   476

    babel 是一个 js 到 js 的转译器,可以通过语法插件支持 es next、typescript、flow 等语法,支持 AST 转换插件,最后生成目标代码和 sourcemap。

    从源码聊 postcss 和 babel 的 api 风格的不同

    postcss 是一个 css 到 css 的转译器,可以通过语法插件支持 less、sass、stylus 等语法,也支持 AST 转换插件,最后生成目标代码和 sourcemap。

    从源码聊 postcss 和 babel 的 api 风格的不同

    postcss 之于 css,就像 babel 之于 js 一样,postcss 的插件体系也很繁荣,比如 autoprefixer、stylelint 等。

    这俩工具在定位上类似,都是用于代码的转译和静态分析。但是在 api 设计上有所不同。

    学完这篇文章你会了解到:

    • babel、postcss 的 api 的简易使用
    • babel、postcss 的编译流程
    • postcss 的源码架构
    • postcss 和 babel 在 api 设计上的不同
    • 链式和集中式的 api 风格各有什么好处

    babel api

    我们先从熟悉的 babel 开始,babel 7 的 api 一般这样用:

    const babel = require('@babel/core');
    
    const code = `
        console.log('hello world');
    `;
    const { code, map } = babel.transformSync(code, {
        plugins: [
            [
                pluginA,
                {
                    // options
                }
            ]
        ],
        sourceMaps: true
    });
    

    使用 babel core 包的 transformSync 的 api,传入源码和转换插件。babel 会在内部完成源码到 AST 的 parseAST 的 transform,以及目标代码和 sourcemap 的 generate 三个阶段。

    我们可以用 babel3 的源码来了解下内部做了什么(babel3 的源码比较清晰,感兴趣可以去看看):

    首先,babel 通过 File 对象来保存处理的文件的源码等信息,然后调用 parse 从源码聊 postcss 和 babel 的 api 风格的不同

    parse 方法里面会完成 parse、transform、generate 这 3 步:

    从源码聊 postcss 和 babel 的 api 风格的不同

    而 parse 在 babel 3 的时候还是直接使用了 acorn(babel 7 的 parser 是 fork 了 acorn 做的修改):

    从源码聊 postcss 和 babel 的 api 风格的不同

    transform 阶段会调用所有内置的 transformer 插件对 AST 进行转换:

    从源码聊 postcss 和 babel 的 api 风格的不同

    而 generate 阶段会生成目标代码和 sourcemap:

    从源码聊 postcss 和 babel 的 api 风格的不同

    babel 后续做了很多的迭代,比如分成了很多包,但是这个流程是没有变的。

    我们有的时候也会直接用更底层的包,而不使用 babel core:

    const parser = require('@babel/parser');
    const traverse = require('@babel/traverse').default;
    const generate = require('@babel/generator').default;
    
    const code = `
        console.log('hello world');
    `;
    
    const ast = parser.parse(code);
    
    traverse(ast, {
        visitor: {
            CallExpression(path) {
                //xxx
            }
        }
    });
    
    const { code, map } = generate(ast, {
        sourceMaps: true
    });
    

    其实 @babel/core 是基于 @babel/parser、@babel/traverse、@babel/generator 等包做的封装,支持了插件能力。如果不需要插件,那么直接用这些包就行。

    postcss api

    postcss 的 api 是这样的:

    const postcss = require('postcss');
    const autoprefixer = require('autoprefixer')
    
    const css = `
    @import "aaa";
    a {
       background: color;
    }`;
    
    postcss([autoprefixer]).process(css).then(result => {
     console.log(result.css)
    })  
    

    首先调用 postcss 函数传入插件数组,然后调用 process 方法处理传入的 css,再调用 then 就可以在回调里面拿到结果(目标代码和 sourcemap)。

    为什么会是这样的顺序呢,我们从源码角度看一下:

    从源码聊 postcss 和 babel 的 api 风格的不同

    首先调用的 postcss 方法返回 Processor 对象,而 Processor 对象有 use(应用插件)、process(处理 css) 等方法,所以才是 postcss([]).process

    从源码聊 postcss 和 babel 的 api 风格的不同

    然后我们可以看到 process 返回的值是 LazyResult 对象,而 LazyResult 对象会调用 parser 对传入的 css 进行 parse,提供了 then 方法来返回结果,所以是 postcss([]).process(css).then(() => {})。

    then 里面会调用 aync,会应用插件,处理 AST(root),最终返回 stringifier 的结果。

    从源码聊 postcss 和 babel 的 api 风格的不同

    stringifier 里会调用 MapGenerator 把 AST 打印成目标 css,并生成 sourcemap。

    从源码聊 postcss 和 babel 的 api 风格的不同

    这就是 postcss 的编译流程。

    postcss 其实依然是 parse、transform、generate 3 步,只不过 api 不同。

    从源码聊 postcss 和 babel 的 api 风格的不同

    postcss 内部有这些类:

    • Processor:传入插件,返回 LazyResult,有 use(传入插件)、process(返回结果) 方法
    • LazyResult:传入源码,调用 Parser 对源码进行 parse,有 then(异步获取结果)方法,会用插件对 AST 转换,然后调用 MapGenerator 打印 AST
    • Parser:传入源码,返回 AST
    • MapGenerator: 传入 AST,打印成目标代码,并生成 sourcemap。

    再回到开始,我们调用的方式是

    postcss([autoprefixer]).process(css).then(result => {
     console.log(result.css)
    }) 
    

    其实 postcss 就是个工厂函数,用于创建 Processsor 对象,Processor 对象的 process 方法返回 LazyResult 对象,LazyResult 对象有 then 方法来返回结果。

    链式和集中式的 api 风格

    postcss 和 babel 都是对代码(js、css)的转译,但是 api 却差别挺大,来对比下:

    babel 是:

    const { code, map } = babel.transformSync(code, {
        plugins: [
            [
                pluginA,
                {
                    // options
                }
            ]
        ],
        sourceMaps: true
    });
    

    postcss 是:

    postcss([autoprefixer]).process(css).then(result => {
     console.log(result.css)
    }) 
    

    可以看到 babel 只有一个 api,一次性传入源码和插件,在内部完成 parse、transform、generate 3个阶段。

    postcss 则是提供了链式的 api,分多步传入插件和源码。

    postcss 这种链式的风格的好处是代码紧凑,每一步做了什么都比较明确。babel 这种一次性传入的好处是使用简单,但是 option 耦合在一块,复杂度高。

    链式的多次传入,还是集中式的一次传入,都有自己的好处,比如 jquery、chalk、jest、webpack-chain 都是链式的,而 webpack、babel 等都是一次性传入的。

    jest api 风格(链式):

    test(`jest`, () => {
        expect(a + b).not.toBeLessThan(expected);
    }
    

    chalk api 风格(链式):

    chalk.rgb(123, 45, 67).underline('Underlined reddish color')
    

    webpack-chain api 风格(链式):

    config
      .entry('index')
        .add('src/index.js')
        .end()
      .output
        .path('dist')
        .filename('[name].bundle.js');
    

    webpack api 风格(集中式):

    webpack({
      entry: './src/index.js',
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js',
      },
    })
    

    总结

    本文我们通过 babel 和 postcss 的 api 用法分别探究了下内部的实现流程,知道了为什么 postcss 是 postcss([plugins]).process(css).then 的调用链。

    之后分别梳理了 babel 的集中式风格 api、postcss 的链式风格 api 的优缺点,并且引申出了其他的一些库的 api 风格。知道了其实做同一件事情是可以设计出不同的 api 的。

    希望这篇文章能够让你清楚 postcss 和 babel 内部的流程都是啥,为什么 postcss 的api 调用链条是那样的,它们都是什么 api 风格。链式和集中式都有什么优缺点。


    起源地下载网 » 从源码聊 postcss 和 babel 的 api 风格的不同

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元