抽象语法树(Abstract Syntax Tree)
webpack
和Lint
等很多的工具和库的核心都是通过Abstract Syntax Tree
抽象语法树这个概念来实现对代码的检查、分析等操作的。通过了解抽象语法树这个概念,你也可以随手编写类似的工具
#
2.抽象语法树用途- 代码语法的检查、代码风格的检查、代码的格式化、代码的高亮、代码错误提示、代码自动补全等等
- 如JSLint、JSHint对代码错误或风格的检查,发现一些潜在的错误
- IDE的错误提示、格式化、高亮、自动补全等等
- 代码混淆压缩
- UglifyJS2等
- 优化变更代码,改变代码结构使达到想要的结构
- 代码打包工具webpack、rollup等等
- CommonJS、AMD、CMD、UMD等代码规范之间的转化
- CoffeeScript、TypeScript、JSX等转化为原生Javascript
#
3.抽象语法树定义这些工具的原理都是通过JavaScript Parser把代码转化为一颗抽象语法树(AST),这颗树定义了代码的结构,通过操纵这颗树,我们可以精准的定位到声明语句、赋值语句、运算语句等等,实现对代码的分析、优化、变更等操作
在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。
Javascript的语法是为了给开发者更好的编程而设计的,但是不适合程序的理解。所以需要转化为AST来更适合程序分析,浏览器编译器一般会把源码转化为AST来进行进一步的分析等其他操作。
4.JavaScript Parser [#](#t34.JavaScript Parser)
JavaScript Parser,把js源码转化为抽象语法树的解析器。
浏览器会把js源码通过解析器转为抽象语法树,再进一步转化为字节码或直接生成机器码。
一般来说每个js引擎都会有自己的抽象语法树格式,Chrome的v8引擎,firefox的SpiderMonkey引擎等等,MDN提供了详细SpiderMonkey AST format的详细说明,算是业界的标准。
4.1 常用的JavaScript Parser有: [#](#t44.1 常用的JavaScript Parser有:)
- esprima
- traceur
- acorn
- shift
4.2 esprima [#](#t54.2 esprima)
通过 esprima 把源码转化为AST
通过 estraverse 遍历并更新AST
通过 escodegen 将AST重新生成源码
-
let esprima = require('esprima'); var estraverse = require('estraverse'); var escodegen = require("escodegen"); let code = 'function ast(){}'; let ast = esprima.parse(code); console.log(ast); estraverse.traverse(ast,{ enter(node){ node.name += '_ext'; }, leave(node){ // console.log(node.type); } }); let generated = escodegen.generate(ast); console.log(generated);
5. 转换箭头函数 [#](#t65. 转换箭头函数)
访问者模式Visitor 对于某个对象或者一组对象,不同的访问者,产生的结果不同,执行操作也不同
-
let babel = require('babel-core'); let types = require('babel-types'); const code = `const sum = (a,b)=>a+b`; // babel-eslint const visitor = { ArrowFunctionExpression:{ enter(path) { let node = path.node; let expression = node.body; let params = node.params; let returnStatement = types.returnStatement(expression); let block = types.blockStatement([ returnStatement ]); let func = types.functionExpression(null,params, block,false, false); path.replaceWith(func); } } } const result = babel.transform(code,{ plugins:[ {visitor} ] }); console.log(result.code);
6. babel插件 [#](#t76. babel插件)
预计算简单表达式的插件
const result = 1 + 2;
const result = 3;
let babel = require('babel-core');
let types = require('babel-types');
let visitor = {
BinaryExpression(path){
let node = path.node;
if(!isNaN(node.left.value) && !isNaN(node.right.value)){
let result = eval(node.left.value + node.operator + node.right.value);
path.replaceWith(types.numericLiteral(result));
if(path.parentPath&& path.parentPath.node.type == 'BinaryExpression'){
visitor.BinaryExpression.call(null,path.parentPath);
}
}
}
}
module.exports = function(babel){
return {
visitor
}
}
const babel = require('babel-core');
const result = babel.transform('const sum = 1+2+3',{
plugins:[
require('./plugin')
]
});
console.log(result.code);
7. webpack babel插件 [#](#t87. webpack babel插件)
7.1 实现按需加载 [#](#t97.1 实现按需加载)
import { flatten,concat } from "lodash"
转换为
import flatten from "lodash/flatten";
import concat from "lodash/flatten";
7.2 babel配置 [#](#t107.2 babel配置)
transform-runtime Babel 默认只转换新的 JavaScript 语法,而不转换新的 API。例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转译,启用插件 babel-plugin-transform-runtime 后,Babel 就会使用 babel-runtime 下的工具函数
{ "presets":["react","stage-0","env"], "plugins":[ ["extract", {"library":"lodash"}], ["transform-runtime", {}] ] }
编译顺序为首先
plugins
从左往右,然后presets
从右往左
7.3 babel插件 [#](#t117.3 babel插件)
let babel = require('babel-core');
let types = require('babel-types');
const visitor = {
ImportDeclaration:{
enter(path,ref={opts:{}}){
const specifiers = path.node.specifiers;
const source = path.node.source;
if(ref.opts.library == source.value && !types.isImportDefaultSpecifier(specifiers[0])){
const declarations = specifiers.map((specifier,index)=>{
return types.ImportDeclaration(
[types.importDefaultSpecifier(specifier.local)],
types.stringLiteral(`${source.value}/${specifier.local.name}`)
)
});
path.replaceWithMultiple(declarations);
}
}
}
}
module.exports = function(babel){
return {
visitor
}
}
Gitalking ...
Be the first guy leaving a comment!