前言
在如今前端项目开发中,已经离不开babel的参与,很多时候我们都只是用了babel的预设presets
,几乎都没有修改过它的配置,也不曾去自己手写一个babel plugin,对它的了解都只是停留在使用的地步,在此,让我们动起来,自己去实现一个babel plugin去感受一下babel是如何处理。
babel
babel是什么?
Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
babel能做什么?
- 语法转换
- 通过 Polyfill 方式在目标环境中添加缺失的特性 (通过引入第三方 polyfill 模块,例如 core-js)
- 源码转换(codemods)
babel原理
babel的转码过程其实就是三步走:解析(parse)、转换(transform)、和生成(gennerate)。这三步分别由 @babel/parser
、@babel/core
(大集合一个) 和 @babel/generator
来完成。
解析(parse)
在此阶段,babel会把源码解析生成AST(抽象语法树),此步骤由两部分组成:词法分析与语法分析。词法分析会将字符串形式的代码转换成 tokens 流, 语法分析会将 tokens 流转换成AST。
code -> tokens -> AST
babel解析器以前使用的是 Babylon,到了 babel7 使用的是由 Babylon 发展而来的 @babel/parser
转换(transform)
重点:
经过了上一步的处理,我们得到了 AST,开始开刀,做我们需要的调整。
- 通过
@babel/traverse
对AST进行遍历。 - 找到我们需要修改/替换.....的点。
- 通过
@babel/types (babel的一个工具库)
对AST进行修改操作。 - 生成新的 AST。
生成(gennerate)
我们得到修改后的 AST 然后需要用 @babel/generator
把 AST 转换成代码。
实现一个 babel 插件
说了babel的转换过程,现在是时候来动手来实现一个插件,我们在开发的工程中,不免会使用 console
来输出某些信息,但是在线上环境,一般都是会把这些信息输出给删除掉。
我们随意创建一点小代码
function foo() {
const num = 123;
console.log(num);
}
然后通过这里在线转成 AST
观察它的结构
创建一个babel插件文件,直接在babel的plugin使用我们新创建的plgun文件
然后简单实现,我们可以通过 @babel/types
里面提供的方法去检测 AST 的某些节点,然后找到相应的节点删除
module.exports = function({ types: t }) {
return {
name: "clear-console",
visitor: {
CallExpression(path) {
const callee = path.node.callee;
if (!t.isMemberExpression(callee)) {
return;
}
if (t.isIdentifier(callee.object, { name: 'console' })) {
path.remove();
}
}
}
}
}
至此,简单的插件就写好了,当然,我们也可以拓展,可传入参数,也可以获取 process.env.NODE_ENV
的值判断是否在正式环境启动删除 console
我们传入一个 isRemove 参数来控制是否删除
module.exports = function({ types: t }) {
return {
name: 'clear-console',
visitor: {
CallExpression(path, state) {
const callee = path.node.callee;
const { opts } = state;
// 我们获取参数做判断
if (!opts.isRemove) {
return;
}
if (!t.isMemberExpression(callee)) {
return;
}
if (t.isIdentifier(callee.object, { name: 'console' })) {
path.remove();
}
}
}
}
}
结束
以上直接在 react 项目中,写一个babel插件。
第一次写文章,写得不好请多见谅。