在现在开发者基本使用ES6+
语法,但是任然存在部分浏览器不能完全支持ES6+
语法,此时我们需要利用babel
来进行转化,将ES6+
语法转化成ES5
语法,来实现浏览器兼容的功能。
babel
是实现代码的转化,而不是对代码进行打包。
下面将从babel
原理方法对其进行解析。
一、babel运行的三个阶段
babel
我们可以把他理解为一个编译器,也就是实现代码转换的功能,其转换过程分为三个阶段。
1、解析: 将js
代码转换成AST
(抽象语法树)
2、转化: 根据抽象语法树进行变换操作
3、生成: 将转换后的抽象语法树生成新的代码
我们在babel.config.js
中设置的一些插件,都是在第二步转化过程使用的。
if (1 > 0) {
alert('hi');
}
经过第一步解析可以得到如下对象。
{
"type": "Program", // 程序根节点
"body": [ // 一个数组包含所有程序的顶层语句
{
"type": "IfStatement", // 一个if语句节点
"test": {
// if语句的判断条件
"type": "BinaryExpression", // 一个双元运算表达式节点
"operator": ">", // 运算表达式的运算符
"left": {
// 运算符左侧值
"type": "Literal", // 一个常量表达式
"value": 1 // 常量表达式的常量值
},
"right": {
// 运算符右侧值
"type": "Literal",
"value": 0
}
},
"consequent": {
// if语句条件满足时的执行内容
"type": "BlockStatement", // 用{}包围的代码块
"body": [ // 代码块内的语句数组
{
"type": "ExpressionStatement", // 一个表达式语句节点
"expression": {
"type": "CallExpression", // 一个函数调用表达式节点
"callee": {
// 被调用者
"type": "Identifier", // 一个标识符表达式节点
"name": "alert"
},
"arguments": [ // 调用参数
{
"type": "Literal",
"value": "hi"
}
]
}
}
]
},
"alternative": null // if语句条件未满足时的执行内容
}
]
}
上面的对象就是我们所说的抽象语法树。
抽象语法树的执行逻辑如下所示。
接下来我们可以进行转换,例如在上面的对象中,我们把IfStatement
改为whileStatement
,就表示把if
语句转化为while
语句。
第三步就是将转化后的AST
树对象转换为js
代码。
二、词法分析
这里第二步和第三步都容易理解的,最主要的是第一步(生成AST
树的过程),生成AST
树主要分为两个部分,第一个是词法分析
,第二个是语法分析
。
词法分析
就是依次遍历全部代码中的每一个字符,然后分析每一个字符的类型,进行判断,最终将其push
进tokens数组中。
function tokenizeCode (code) {
const tokens = []; // 结果数组
for (let i = 0; i < code.length; i++) {
// 从0开始,一个字符一个字符地读取
let currentChar = code.charAt(i);
if (currentChar === ';') {
// 对于这种只有一个字符的语法单元,直接加到结果当中
tokens.push({
type: 'sep',
value: ';',
});
// 该字符已经得到解析,不需要做后续判断,直接开始下一个
continue;
}
if (currentChar === '(' || currentChar === ')') {
// 与 ; 类似只是语法单元类型不同
tokens.push({
type: 'parens',
value: currentChar,
});
continue;
}
if (currentChar === '}' || currentChar === '{') {
// 与 ; 类似只是语法单元类型不同
tokens.push({
type: 'brace',
value: currentChar,
});
continue;
}
if (currentChar === '>' || currentChar === '<') {
// 与 ; 类似只是语法单元类型不同
tokens.push({
type: 'operator',
value: currentChar,
});
continue;
}
if (currentChar === '"' || currentChar === '\'') {
//