babel 是一个 js 到 js 的转译器,可以通过语法插件支持 es next、typescript、flow 等语法,支持 AST 转换插件,最后生成目标代码和 sourcemap。
postcss 是一个 css 到 css 的转译器,可以通过语法插件支持 less、sass、stylus 等语法,也支持 AST 转换插件,最后生成目标代码和 sourcemap。
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 的 parse,AST 的 transform,以及目标代码和 sourcemap 的 generate 三个阶段。
我们可以用 babel3 的源码来了解下内部做了什么(babel3 的源码比较清晰,感兴趣可以去看看):
首先,babel 通过 File 对象来保存处理的文件的源码等信息,然后调用 parse
parse 方法里面会完成 parse、transform、generate 这 3 步:
而 parse 在 babel 3 的时候还是直接使用了 acorn(babel 7 的 parser 是 fork 了 acorn 做的修改):
transform 阶段会调用所有内置的 transformer 插件对 AST 进行转换:
而 generate 阶段会生成目标代码和 sourcemap:
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 方法返回 Processor 对象,而 Processor 对象有 use(应用插件)、process(处理 css) 等方法,所以才是 postcss([]).process
然后我们可以看到 process 返回的值是 LazyResult 对象,而 LazyResult 对象会调用 parser 对传入的 css 进行 parse,提供了 then 方法来返回结果,所以是 postcss([]).process(css).then(() => {})。
then 里面会调用 aync,会应用插件,处理 AST(root),最终返回 stringifier 的结果。
stringifier 里会调用 MapGenerator 把 AST 打印成目标 css,并生成 sourcemap。
这就是 postcss 的编译流程。
postcss 其实依然是 parse、transform、generate 3 步,只不过 api 不同。
postcss 内部有这些类:
Processor
:传入插件,返回 LazyResult,有 use(传入插件)、process(返回结果) 方法LazyResult
:传入源码,调用 Parser 对源码进行 parse,有 then(异步获取结果)方法,会用插件对 AST 转换,然后调用 MapGenerator 打印 ASTParser
:传入源码,返回 ASTMapGenerator
: 传入 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 风格。链式和集中式都有什么优缺点。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!