什么是AST
AST 技术是现代化前端基建和工程化建设的基石,在开发框架,尤其是构建工具中离不开AST的相关技术
AST 的概念
在计算机科学中,抽象语法树(Abstract Syntax Tree),或简称语法树(Syntax Tree),是源代码语法结构的一种抽象表示,它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似if-condition-then 这样的条件跳转语句,可以使用带有三个分支的节点表示
AST 涉及常用的工具
- 构建工具
- webpack
- webpack 也是可以拿到整个的AST ,使用plugin或者loader进行注入
- Babel
- Postcss
- eslint
- webpack
- 开发框架
- Vue
- React
- react的jsx的解析部分就是Babel团队的
AST的整个流程
什么是编译
编译,就是从一种高级语言转换成另一种低级语言
- 高级语言:
Javascript,Java,C,goLang等有很多用于描述逻辑的语言特性,比如 分支、循环、函数、判断等接近人的思维,可以让开发人员快速掌握。 - 低级语言:
汇编语言,机器语言这些和硬件执行细节有关的,会操作寄存、内存。需要开发者理解熟悉计算机的工作原理。
前端的编译
前端的编译很多是落脚在 ES6 - ES5
AST生成过程
Parser
source code -> tokenizer -> 分词结果 -> 语法分析 -> AST
transform
generator
常见的AST节点
AST 也是有标准的,JS parser的AST大多是estree标准,Babel 的编译也是遵循着这个标准来的
1.Literal
字面量
const a ='aa'; //StringLiteral
const b = 'aa'; // templateLiteral
const c = 123; //NumberLiteral
const reg = /^[a-z]+/;//RegExpLiteral
const d = true; //BooleanLiteral
const e = null; //NullLiteral
2. Identifier
标识符:变量名、属性名、参数名等各种声明和引用的名字
3. Statement
语句,它是可以独立执行的单位
break,continue,debugger,return,if,while,for每一个可以独立运行的代码都是语句 一般末尾有分号
- BreakStatement
- ContinueStatement
- DebuggerStatement
- ReturnStatement
- ThrowStatement
{} //BlockState
try()catch(e){} finally() //try
- ForinStatement
- ForStatement
- WhileStatement
- DowhileStatement
4. Declaration
声明语句
const a = 1; //VaribleDeclaration
function (){} //FunctionDeclaration
class C {} //ClassDeclaration
import x form 'xxx' //ImportDeclaration
export
//ExportDefaultDeclaration
//ExportDeclaration
//ExportAllDeclaration
5. Expression
表达式特点是执行完了有返回值
a = 1;
1 + 2
[1,2,3]
AST 的公共属性
start,end ,loc(line,column)
Babel
Babel的用途
转译esnext,typeScript,flow等特定目标环境支持的js
一些特定用途的代码转换
taro转译小程序就是基于babel的API实现的
代码的静态分析
linter 工具,也是基于AST对代码检查 jest ESM不行,需要babel
Babel 的编译流程
- parse:把源码转成AST
- '@babel/parser'
- let name = 'xiaoming';
- tranform:遍历AST,对AST进行改造
- '@babel/traverser'
- 遍历AST,最后调用visitor修改AST
- '@babel/types'
- 创建、修改、删除,需要用到的types
- '@babel/template'
- 如果需要批量处理就需要
- '@babel/traverser'
- generate:把改造后的代码生成目标代码以及sourcemap
- '@babel/generator'
以上这些都是在@babel/core中提供,并基于以上的流程,完成babel的整体编译,并应用plugin 和preset。
简单的打印
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;
const types = require('@babel/types');
const sourceCode = `
console.info(1)
function foo() {
console.log(2)
}
class Bar{
baz(){
console.error(3);
};
render(){
console.debug(4)
}
}
`;
const consoleMembers = ['log', 'error', 'info', 'debug']
// 解析AST
const ast = parser.parse(sourceCode, {
sourceType: 'unambiguous',
});
traverse(ast, {
//visitor
CallExpression(path, state) {
console.log('-----', path.node, state);
if (types.isMemberExpression(path.node.callee)
&& path.node.callee.object.name === 'console'
&& path.node.callee.object.type === 'Identifier'
&& consoleMembers.includes(path.node.callee.property.name)
) {
const { line, column} = path.node.loc.start;
path.node.arguments.unshift(types.stringLiteral(`line:${line},column: ${column}`))
}
}
})
const { code, map } = generate(ast);
console.log(code)