Vue 项目之 Webpack 对其它文件的转化(3)
「这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战」
1. Babel
的底层原理
掌握了 Babel
在命令行中的基本使用之后,我们再来了解一下 Babel
是如何对源代码进行转换的,即 Babel
的编译原理是什么。
-
babel
是如何做到将我们的一段代码(ES6
、TypeScript
、React
)转成另外一段代码(ES5
) 的呢?- 从一种源代码(原生语言)转换成另一种源代码(目标语言),这是什么的工作呢?
- 这就是编译器的工作,事实上我们可以将
babel
看成一个编译器; Babel
编译器的作用就是将我们的源代码转换成浏览器可以直接识别的另外一段源代码;
-
Babel
也拥有编译器的工作流程:-
解析(
Parsing
):将原始代码转化为更抽象的代码表示;-
解析通常会分为两个阶段:
-
词法分析(
Lexical Analysis
):词法分析通过分词器(
tokenizer
)或词法分析器(lexer
)把原始代码拆分成标记(tokens
)。Tokens
是一个数组,里面存放着很小的对象,它们是对语法的一个个独立部分的描述,它们可以是数字、标签、标点符号、操作符等等。 -
语法分析(
Syntactic Analysis
):语法分析把
tokens
重新格式化为描述语法的每个部分及其彼此之间关系的表现形式。这被称为中间表示(intermediate representation
)或抽象语法树(Abstract Syntax Tree
)。抽象语法树(简称
AST
),是一个深度嵌套的对象,它以一种既易于操作又能告诉我们大量信息的方式表示代码。
-
-
比如对于以下语法(
Lisp
语言中计算2 + (4 - 2)
的写法):(add 2 (subtract 4 2))
它经过词法分析后生成的
tokens
可能看起来像这样:[ { type: 'paren', value: '(' }, { type: 'name', value: 'add' }, { type: 'number', value: '2' }, { type: 'paren', value: '(' }, { type: 'name', value: 'subtract' }, { type: 'number', value: '4' }, { type: 'number', value: '2' }, { type: 'paren', value: ')' }, { type: 'paren', value: ')' }, ]
而经过语法分析后生成的抽象语法树(
AST
)可能看起来像这样:{ type: 'Program', body: [ { type: 'CallExpression', name: 'add', params: [ { type: 'NumberLiteral', value: '2' }, { type: 'CallExpression', name: 'subtract', params: [ { type: 'NumberLiteral', value: '4' }, { type: 'NumberLiteral', value: '2' } ] } ] } ] }
-
-
转换(
Transformation
):操作解析出来的这个抽象表示,去做编译器想要它做的任何事; -
代码生成(
Code Generation
):将转换后的代码表示,转换(生成)成新的代码;
-
-
Babel
官网上也有推荐关于编译器的一个很棒的教程:github.com/jamiebuilds…
简单来说,Babel
的执行过程如下:
上面展示的是简化版的编译器工具流程,事实上,在流程的每个阶段都会有具体的工作,而对于 Babel
来说,其更具体的流程如下:
下面再对上面流程图中的有关概念做解析:
- 原始代码:比如说
TypeScript
代码、React
代码、ES6+
的代码等等; - 词法分析(
Lexical Analysis
):在编译器中都会有一个词法分析器,它用来对原始代码中的内容进行分割,然后把分割出来的东西放到一个叫tokens
的数组中; - 语法分析(
Syntactic Analysis
):拿到tokens
数组后,编译器就会开始对tokens
中的内容的语法进行分析,比如说我们有一行js
代码:const name = 'zhj'
,那么语法分析时,就会分析出其中的const
是一个关键字,name
是一个标识符(而关键字是不能作为标识符名称的),=
是一个赋值运算符,'
是一个标点符号,zhj
是一个字符串值。其实在其它语言中也是一样的,都是要经过语法分析的; - 抽象语法树(
AST
):语法分析完成后就会生成一个抽象语法树,它是一个树结构,用来把所有内容通过树结构的形式表示出来; - 遍历(
Traversal
):即遍历抽象语法树中的内容(关键字、标识符等等); - 访问器(
Visitors
):在遍历AST
的过程中,当进入(找到)某个匹配的节点时,我们将会调用自己定义的访问器中的方法; - 插件(
Plugins
):对于Babel
来说,在遍历AST
的过程中,当我们访问到某个节点,比如说在找到const
这个关键字时,就会通过我们应用的插件对const
做转化(比如说转化成var
,具体如何转化和应用的插件有关,插件中会有转换功能对应的代码); - 新的抽象语法树(
new AST
):通过使用插件对AST
进行转换后,会生成新的AST
; - 目标代码:根据新的抽象语法树,生成最终的目标代码;
以上,就是关于 Babel
编译器的原理。
转载自:https://juejin.cn/post/7032590415807119391