携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
Babel的编译流程
整体的编译流程可以分为以下3步:
- 解析(parse):把源码变成AST;
- 转换(transform):遍历AST,通过各种转换插件对AST进行CRUD;
- 生成(generate):把转换后的AST打印成目标代码,并生成sourcemap;
sourcemap:它的作用是为了方便调试。很多时候,为了性能,我们会将代码转换压缩,这样就导致,当代码出现问题的时候,我们很难去调试,因为转换压缩后的代码我们也不知道是哪一行的代码出错。 这个时候,sourcemap就派上用场了。
sourcemap本质上是一个信息文件,它存储的就是代码在转换前后对应的位置关系。说的简单点,就是它是代码转换前后的一道桥梁,方便我们去定位生产环境中bug出现的位置的。
解析(parse)
首先思考,为什么会现有解析这一步,或者说为什么解析会是第一步呢?
我们使用高级语言编写的程序,我们能看得懂,但是计算机却并不能看懂,要想计算机能看懂的话,就要先把我们的代码转换成一种通过不同的对象来保存不同的数据,并且按照依赖关系组织起来的的数据结构,这种数据结构被成为抽象语法树(AST)。抽象语法树抽象的地方在于,它省略掉了一些无具体意义的符号,比如说嵌套括号之类。
也就是说,AST是一种数据结构,一种树形结构。
它是源代码语法结构的一种抽象表示,以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
上面也说了,AST是计算机可以看懂的,计算机可以通过AST理解源码的意思,而只有你理解一件事情,你才能做好一件事情,因此想要进行 转换(transform) ,那必然要先进行代码的解析 (parse) 。
parse阶段所做的事情:把源码字符串转换成机器能理解的AST。 将源码转换成AST的过程种分为词法分析和语法分析两个过程。
词法分析(Lexing)
这个过程回将由字符组成的字符串分解成有意义的代码块(词法单元,token)。
var a = 2;
比如说上面的代码,就被分成了 var 、a 、= 、2 、;五块,至于为什么 var 没有继续分为 v、a、r呢,那是因为在词法分析过程中,是把代码字符串分解成对当前编程语言有意义的代码块,比如上面的代码中, var 是变量声明的关键词,是有意义的,而继续细分出来的代码块 v、a、r 则是无意义的。
语法分析(Parsing)
这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的,代表了程序结构的树(即抽象语法树,AST)。
转换(transform)
当代码转换成AST之后,可以通过修改AST的方式来修改代码,这一步会遍历AST并通过各种Transform插件对AST进行增删改。
在遍历过程中,处理到不同的AST节点,就会去调用注册的相应的 visitor函数, 然后在这个函数中对当前节点进行增删改,最后再返回新的AST(可以指定是否继续遍历新生成的AST),通过这种方法,可以做到遍历完一遍AST之后就完成了对代码的修改。
生成(generate)
经过转换后的AST就是符合我们预期的代码(就是我们想要的代码),然后我们将AST再转变回代码,在此过程中,会把之前删掉的省略掉的一些分隔符加回来。
不同的AST对应不同结构的字符串,这样从AST的根节点进行 递归 的字符串拼接,最后就可以生成目标代码的字符串了。
用小册作者的话来说就是:为了让计算机理解代码需要先对源码字符串进行 parse,生成 AST,把对代码的修改转为对 AST 的增删改,转换完 AST 之后再打印成目标代码字符串。