1.抽象语法树(Abstract Syntax Tree) #

webpackLint等很多的工具和库的核心都是通过Abstract Syntax Tree抽象语法树这个概念来实现对代码的检查、分析等操作的。通过了解抽象语法树这个概念,你也可以随手编写类似的工具

2.抽象语法树用途 #

3.抽象语法树定义 #

这些工具的原理都是通过JavaScript Parser把代码转化为一颗抽象语法树(AST),这颗树定义了代码的结构,通过操纵这颗树,我们可以精准的定位到声明语句、赋值语句、运算语句等等,实现对代码的分析、优化、变更等操作

在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。

Javascript的语法是为了给开发者更好的编程而设计的,但是不适合程序的理解。所以需要转化为AST来更适合程序分析,浏览器编译器一般会把源码转化为AST来进行进一步的分析等其他操作。

ast

4.JavaScript Parser #

4.1 常用的JavaScript Parser有: #

4.2 esprima #

5. 转换箭头函数 #

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插件 #

预计算简单表达式的插件

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插件 #

7.1 实现按需加载 #

 import { flatten,concat } from "lodash"

转换为

import flatten from "lodash/flatten";
import concat from "lodash/flatten";

7.2 babel配置 #

{
        "presets":["react","stage-0","env"],
        "plugins":[
            ["extract", {"library":"lodash"}],
            ["transform-runtime", {}]
        ]
}

编译顺序为首先plugins从左往右,然后presets从右往左

7.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
    }
}

备注 #