来源:公司资讯 | 2021.08.19
1、简略描绘一下 Babel 的编译进程?
Babel 是一个源到源的转化编译器(Transpiler),它的首要作用是将 JavaScript 的高版别语法(例如 ES6)转化成低版别语法(例如 ES5),然后可以适配浏览器的兼容性。
温馨提示:假设某种高级言语或许运用言语(例如用于人工智能的计算机规划言语)转化的政策言语不是特定计算机的汇编言语,而是面向另一种高级程序言语(许多研究性的编译器将 C 作为政策言语),那么还需求将政策高级程序言语再进行一次额外的编译才干得到终究的政策程序,这种编译器可称为源到源的转化器。
从上图可知,Babel 的编译进程首要可以分为三个阶段:
解析(Parse):包括词法分析和语法分析。词法分析首要把字符流源代码(Char Stream)转化成令牌流( Token Stream),语法分析首要是将令牌流转化成笼统语法树(Abstract Syntax Tree,AST)。
转化(Transform):经过 Babel 的插件才干,将高版别语法的 AST 转化成支撑低版别语法的 AST。当然在此进程中也可以对 AST 的 Node 节点进行优化操作,比如增加、更新以及移除节点等。
生成(Generate):将 AST 转化成字符串形式的低版别代码,一同也能创立 Source Map 映射。
具体的流程如下所示:
举个栗子,假设要将 TypeScript 语法转化成 ES5 语法:
// 源代码
let a: string = 1;
// 政策代码
var a = 1;
拷贝代码
1
2
3
4
5
1.1 解析(Parser)
Babel 的解析进程(源码到 AST 的转化)可以运用 @babel/parser,它的首要特色如下:
支撑解析最新的 ES2020
支撑解析 JSX、Flow & TypeScript
支撑解析实验性的语法提案(支撑任何 Stage 0 的 PRS)
@babel/parser 首要是根据输入的字符串流(源代码)进行解析,最终转化成规范(根据 ESTree 进行调整)的 AST,如下所示:
import { parse } from '@babel/parser';
const source = `let a: string = 1;`;
enum ParseSourceTypeEnum {
Module = 'module',
Script = 'script',
Unambiguous = 'unambiguous',
}
enum ParsePluginEnum {
Flow = 'flow',
FlowComments = 'flowComments',
TypeScript = 'typescript',
Jsx = 'jsx',
V8intrinsic = 'v8intrinsic',
}
// 解析(Parser)阶段
const ast = parse(source, {
// 严峻形式下解析而且答应模块定义
sourceType: ParseSourceTypeEnum.Module,
// 支撑解析 TypeScript 语法(留心,这儿只是支撑解析,并不是转化 TypeScript)
plugins: [ParsePluginEnum.TypeScript],
});
需求留心,在 Parser 阶段首要是进行词法和语法分析,假设词法或许语法分析错误,那么会在该阶段被检测出来。假设检测正确,则可以进入语法的转化阶段。
1.2 转化(Transform)
Babel 的转化进程(AST 到 AST 的转化)首要运用 @babel/traverse,该库包可以经过访问者形式主动遍历并访问 AST 树的每一个 Node 节点信息,然后完成节点的替换、移除和增加操作,如下所示:
import { parse } from '@babel/parser';
import traverse from '@babel/traverse';
enum ParseSourceTypeEnum {
Module = 'module',
Script = 'script',
Unambiguous = 'unambiguous',
}
enum ParsePluginEnum {
Flow = 'flow',
FlowComments = 'flowComments',
TypeScript = 'typescript',
Jsx = 'jsx',
V8intrinsic = 'v8intrinsic',
}
const source = `let a: string = 1;`;
// 解析(Parser)阶段
const ast = parse(source, {
// 严峻形式下解析而且答应模块定义
sourceType: ParseSourceTypeEnum.Module,
// 支撑解析 TypeScript 语法(留心,这儿只是可以解析,并不是转化 TypeScript)
plugins: [ParsePluginEnum.TypeScript],
});
// 转化(Transform) 阶段
traverse(ast, {
// 访问变量声明标识符
VariableDeclaration(path) {
// 将 const 和 let 转化为 var
path.node.kind = 'var';
},
// 访问 TypeScript 类型声明标识符
TSTypeAnnotation(path) {
// 移除 TypeScript 的声明类型
path.remove();
},
});
关于 Babel 中的访问器 API,这儿不再过多说明,假设想了解更多信息,可以检查 Babel 插件手册。除此之外,你可能现已留心到这儿的转化逻辑其实可以理解为完成一个简略的 Babel 插件,只是没有封装成 Npm 包。当然,在实在的插件开发开发中,还可以合作 @babel/types 工具包进行节点信息的判别处理。
温馨提示:这儿只是简略的一个 Demo 示例,在实在转化 let、const 等变量声明的进程中,还会遇到处理暂时性死区(Temporal Dead Zone, TDZ)的状况,更多具体信息可以检查官方的插件 babel-plugin-transform-block-scoping。