Babel转换流程详解

1,284 阅读2分钟

Babel是什么?

Babel 是一个 JavaScript 编译器

Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

Babel是如何编译的?

编译主要分为三个阶段:

  1. parse:解析--将代码字符串解析成AST
  2. transform:转换--对AST进行变换操作
  3. generate:生成--根据转换后的AST再生成代码字符串

image.png

接下来对这几个阶段一一来介绍

parse:@babel/parser

@babel/parser:JavaScript解析器(以前称为 Babylon),主要使用其API--parse()对源码进行词法分析语法分析最终转换成AST

api:babelParser.parse(code, [options])

// @babel/parser/lib/index.js源码 parse方法如下
function parse(input, options) {
  var _options;
  if (((_options = options) == null ? void 0 : _options.sourceType) === "unambiguous") {
    options = Object.assign({}, options);
    try {
      options.sourceType = "module";
      const parser = getParser(options, input);
      const ast = parser.parse();
      if (parser.sawUnambiguousESM) {
        return ast;
      }
      if (parser.ambiguousScriptDifferentAst) {
        try {
          options.sourceType = "script";
          return getParser(options, input).parse();
        } catch (_unused) {}
      } else {
        ast.program.sourceType = "script";
      }
      return ast;
    } catch (moduleError) {
      try {
        options.sourceType = "script";
        return getParser(options, input).parse();
      } catch (_unused2) {}
      throw moduleError;
    }
  } else {
    return getParser(options, input).parse();
  }
}
入参options
  • plugins:包含要启用的插件的数组。
  • sourceType:(script|module|unambiguous)代码应该被解析的模式,默认为“script”。 “unambiguous”会使@babel/parser 尝试根据 ES6 导入或导出语句的存在进行猜测。 带有 ES6 导入和导出的文件被视为“module”,否则被视为“script”。
Output

基于ESTree规范格式生成AST

参考下面的代码和图:右边是入参options,左边是生成的AST

const add = (a,b)=>{
    return a + b
}
add(1,2)

image.png

transform:@babel/core、@babel/traverse、@babel/types

对AST语法树进行替换,调用插件@babel/core下面transform相关方法,但是transform其实是使用@babel/traverse插件对AST语法进行节点的替换、增删改查等操作

const parser = require("@babel/parser");
const core = require("@babel/core");
const traverse = require("@babel/traverse");
const types = require("@babel/types");
const code = `const add = (a,b)=>{
    return a + b
}
add(1,2)`;
// options参数参考链接
// https://www.babeljs.cn/docs/options
const options = {
    caller: {
      name: 'babel-loader',
      target: 'web',
      supportsStaticESM: true,
      supportsDynamicImport: true,
      supportsTopLevelAwait: true
    }
}
// 1、词法分析语法分析转换成AST
const ast = parser.parse(code);
// transform
core.transformFromAst(ast, code, options, function(err, result) {
   // result==>code, map, ast }
   return result
});

// 2、对AST节点进行增、删、改、查操作
traverse.default(ast, {
    enter(path) {
        // isArrowFunctionExpression等其实是依赖babel/types插件方法
        if (path.isArrowFunctionExpression(path.node)) {
            path.node.type = 'FunctionExpression'
        }
    }
})

1. @babel/traverse: 是一个对AST进行遍历的工具。类似字符串的replace方法,指定一个正则表达式,就能对字符串进行替换。只不过babel-traverse是针对ast进行替换。

2.@babel/types: 检查AST节点类型和生成对应的表达式,比如判断AST节点是不是箭头函数类型:type.isArrowFunctionExpression(path.node)

image.png

generator:@babel/generator

使用插件@babel/generator将转换好的ast重新生成目标代码。

const generate = require("@babel/generator");

const output = generate(ast,options,code)

image.png