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

2.抽象语法树用途 #

3.抽象语法树定义 #

ast

4. JavaScript Parser #

4.1 常用的 JavaScript Parser #

4.2 遍历 #

cnpm i esprima estraverse- S
let esprima = require('esprima');//把JS源代码转成AST语法树
let estraverse = require('estraverse');///遍历语法树,修改树上的节点
let escodegen = require('escodegen');//把AST语法树重新转换成代码
let code = `function ast(){}`;
let ast = esprima.parse(code);
let indent = 0;
const padding = ()=>" ".repeat(indent);
estraverse.traverse(ast,{
    enter(node){
        console.log(padding()+node.type+'进入');
        if(node.type === 'FunctionDeclaration'){
            node.id.name = 'newAst';
        }
        indent+=2;
    },
    leave(node){
        indent-=2;
        console.log(padding()+node.type+'离开');
    }
});
Program进入
  FunctionDeclaration进入
    Identifier进入
    Identifier离开
    BlockStatement进入
    BlockStatement离开
  FunctionDeclaration离开
Program离开

5.babel #

ast-compiler-flow.jpg

5.1 AST遍历 #

5.2 babel 插件 #

5.2.1 转换箭头函数 #

转换前

const sum = (a,b)=>{
    console.log(this);
    return a+b;
}

转换后

var _this = this;

const sum = function (a, b) {
  console.log(_this);
  return a + b;
};
npm i @babel/core babel-types -D

实现

let core = require('@babel/core');
let types = require('babel-types');
let  BabelPluginTransformEs2015ArrowFunctions = require('babel-plugin-transform-es2015-arrow-functions');
const sourceCode = `
const sum = (a,b)=>{
    console.log(this);
    return a+b;
}
`;
//babel插件其实是一个对象,它会有一个visitor访问器
let BabelPluginTransformEs2015ArrowFunctions2 = {
    //每个插件都会有自己的访问器
    visitor:{
        //属性就是节点的类型,babel在遍历到对应类型的节点的时候会调用此函数
        ArrowFunctionExpression(nodePath){//参数是节点的数据
            let node = nodePath.node;//获取 当前路径上的节点
            //处理this指针的问题
            hoistFunctionEnvironment(nodePath);
            node.type = 'FunctionExpression';
        }
    }
}
function hoistFunctionEnvironment(fnPath){
   const thisEnvFn = fnPath.findParent(p=>{
       //是一个函数,不能是箭头函数 或者 是根节点也可以
       return (p.isFunction() && !p.isArrowFunctionExpression())||p.isProgram()
   });
   //找一找当前作用域哪些地方用到了this的路径
   let thisPaths = getScopeInfoInformation(fnPath);
   //声明了一个this的别名变量,默认是_this __this
   let thisBinding = '_this';
   if(thisPaths.length>0){
       //在thisEnvFn的作用域内添加一个变量,变量名_this,初始化的值为this
    thisEnvFn.scope.push({
        id:types.identifier(thisBinding),
        init:types.thisExpression()    
    });
    thisPaths.forEach(item=>{
        //创建一个_this的标识符  
        let thisBindingRef = types.identifier(thisBinding);
        //把老的路径 上的节点替换成新节点
        item.replaceWith(thisBindingRef);
    });
   }
}
function getScopeInfoInformation(fnPath){
  let thisPaths = [];
  //遍历当前path所有的子节点路径,
  //告诉 babel我请帮我遍历fnPath的子节点,遇到ThisExpression节点就执行函数,并且把对应的路径传进去 
  fnPath.traverse({
    ThisExpression(thisPath){
        thisPaths.push(thisPath);
    }
  });
  return thisPaths;
}

let targetCode = core.transform(sourceCode,{
    plugins:[BabelPluginTransformEs2015ArrowFunctions2]
});
console.log(targetCode.code);

5.2.2 把类编译为 Function #

es6

class Person {
  constructor(name) {
    this.name = name;
  }
  getName() {
    return this.name;
  }
}

classast

es5

function Person(name) {
  this.name = name;
}
Person.prototype.getName = function () {
  return this.name;
};

es5class1 es5class2

实现

//核心库,提供了语法树的生成和遍历的功能
let babel = require('@babel/core');
//工具类,可能帮我们生成相应的节点
let t = require('babel-types');
//babel_plugin-transform-classes
let TransformClasses = require('@babel/plugin-transform-classes');
let es6Code = `class Person{
    constructor(name){
        this.name = name;
    }
    getName(){
        return this.name;
    }
}
`;
let transformClasses2 = {
    visitor: {
        ClassDeclaration(nodePath) {
            let {node} = nodePath;
            let id = node.id;//{type:'Identifier',name:'Person'}
            console.log(id);
            let methods = node.body.body;
            let nodes = [];
            methods.forEach(classMethod=>{
                if(classMethod.kind === 'constructor'){
                    let constructorFunction = t.functionDeclaration(
                        id, classMethod.params, classMethod.body,
                         classMethod.generator, classMethod.async);
                         nodes.push(constructorFunction);
                }else{
                    let prototypeMemberExpression = t.memberExpression(id, t.identifier('prototype'));
                    let keyMemberExpression = t.memberExpression(prototypeMemberExpression, classMethod.key);
                    let memberFunction = t.functionExpression(
                        id, classMethod.params, classMethod.body,
                         classMethod.generator, classMethod.async);
                    let assignmentExpression=t.assignmentExpression("=", 
                    keyMemberExpression,
                    memberFunction);
                    nodes.push(assignmentExpression);
                }
            });
            if(nodes.length==1){
                nodePath.replaceWith(nodes[0]);
            }else if(nodes.length>1){
                nodePath.replaceWithMultiple(nodes);
            }
        }
    }
}


let es5Code = babel.transform(es6Code,{
    plugins:[transformClasses2]
});
console.log(es5Code.code);

6. webpack TreeShaking插件 #

var babel = require("@babel/core");
let { transform } = require("@babel/core");

6.1 实现按需加载 #

import { flatten, concat } from "lodash";

treeshakingleft

转换为

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

treeshakingright

6.2 webpack 配置 #

cnpm i webpack webpack-cli -D
const path = require("path");
module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve("dist"),
    filename: "bundle.js",
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader",
          options:{
                   plugins:[
                     [
                       path.resolve(__dirname,'plugins/babel-plugin-import.js'),
                       {
                         libraryName:'lodash'
                       }
                     ]
                   ]
                }
        },
      },
    ],
  },
};

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

6.3 babel 插件 #

plugins\babel-plugin-import.js

let babel = require("@babel/core");
let types = require("babel-types");
const visitor = {
  ImportDeclaration: {
    enter(path, state = { opts }) {
      const specifiers = path.node.specifiers;
      const source = path.node.source;
      if (
        state.opts.libraryName == 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,
  };
};

7. 参考 #