我们再来看一下,babel 如何把类声明转换成函数声明,然后也来实现这样一个插件。

安装包

  • @babel/core babel 核心模块
  • @babel/types 用来生成或者判断节点的AST语法树的节点,如何创建?。
  • @babel/plugin-transform-classes 转换插件
    1. yarn @babel/core @babel/types @babel/plugin-transform-classes --dev

    使用

    ```javascript // babel 核心模块 const core = require(‘@babel/core’); //用来生成或者判断节点的AST语法树的节点 const types = require(‘@babel/types’);

const transformClassesPlugin = require(‘@babel/plugin-transform-classes’);

const sourceCode = class Person { constructor(name){ this.name = name; } sayName(){ console.log(this.name); } } const targetSource = core.transform(sourceCode, { plugins: [transformClassesPlugin] });

console.log(targetSource.code);

/** 目标代码

  • function Person(){
  • this.name = name;
  • }
  • Person.prototype.sayName = function(){
  • console.log(this.name);
  • } */ ``` 转换后的结果
    image.png

    手写转换插件

    为了更好的理解我们写的代码,所以我们把抽象语法树都贴出来,对照下前后的区别。

    类的 AST

    image.png

    函数的 AST

    image.png

    重写 transformClassesPlugin

    通过上面 AST 对比,我们基本知道要构建什么样的 AST,如果有兴趣自己去网站中贴了代码看一下。 ```javascript //…. const transformClassesPlugin = { visitor: { // 如果是类声明,那么就会进来此函数,参数是类声明的节点路径对象 ClassDeclaration(path){ // 获取路径上的节点对象 const node = path.node; // 获取节点的标识符 const id = node.id; // 获取 class 中的方法 const methods = node.body.body; // 定义一个 nodes 数组存放转换过后的节点 const nodes = []; methods.forEach(method => {

     // 如果类型是一个构造函数
     if(method.kind === 'constructor'){
       // 通过types 创建一个函数声明。
       const constructorFunction = types.FunctionDeclaration(
         id,
         method.params,
         method.body
       );
       nodes.push(constructorFunction);
     }else{
       const memberExpression = types.memberExpression(
         types.memberExpression(
           id,
           types.identifier('prototype')
         ), 
         method.key
       );
       const functionExpression = types.functionExpression(
         null,//method.key,
         method.params,
         method.body
       );
    
       // 第二个节点是一个赋值表达式
       //        左边是成员表达式
       //        右边是函数表达式
       const assignmentExpression = types.assignmentExpression(
         '=',
         memberExpression,
         functionExpression
       );
       nodes.push(assignmentExpression);
     }
    

    });

    if(nodes.length === 0){

     //单节点用 replaceWith
     //path 代表路径,用 node[0] 这个新节点替换旧path上现有老节点 node ClassDeclaration
     path.replaceWith(nodes[0]);
    

    }else{

     //多节点用 replaceWithMultiple
     path.replaceWithMultiple(nodes);
    

    } } } }

//….

<a name="FgKPs"></a>
### 完整代码
```javascript
// babel 核心模块
const core = require('@babel/core');
//用来生成或者判断节点的AST语法树的节点
const types = require('@babel/types');

//const transformClassesPlugin = require('@babel/plugin-transform-classes');
const transformClassesPlugin = {
  visitor: {
    // 如果是箭头函数,那么就会进来此函数,参数是箭头函数的节点路径对象
    ClassDeclaration(path){
      const node = path.node;
      const id = node.id;
      const methods = node.body.body;
      const nodes = [];
      methods.forEach(method => {
        if(method.kind === 'constructor'){
          console.log(method.params)
          const constructorFunction = types.functionDeclaration(id, method.params, method.body);
          nodes.push(constructorFunction);
        }else{
          let memberExpression = types.memberExpression(
            types.memberExpression(
              id,
              types.identifier('prototype')
            ), method.key
          )
          const functionExpression = types.functionExpression(
            null,//method.key,
            method.params,
            method.body
          )
          let assignmentExpression = types.assignmentExpression(
            '=',
            memberExpression,
            functionExpression
          )
          nodes.push(assignmentExpression);
        }
      })
      if(node.length === 1){
        //单节点用 replaceWith
        //path 代表路径,用 node[0] 这个新节点替换旧path上现有老节点 node ClassDeclaration
        path.replaceWith(nodes[0]);
      }else{
        //多节点用 replaceWithMultiple
        path.replaceWithMultiple(nodes);
      }

    }
  }
}

const sourceCode = `
class Person {
  constructor(name){
    this.name = name;
  }
  sayName(){
    console.log(this.name);
  }
}
`
const targetSource = core.transform(sourceCode, {
  plugins: [transformClassesPlugin]
});

console.log(targetSource.code);

结果

image.png