我们再来看一下,babel 如何把类声明转换成函数声明,然后也来实现这样一个插件。
安装包
- @babel/core babel 核心模块
- @babel/types 用来生成或者判断节点的AST语法树的节点,如何创建?。
- @babel/plugin-transform-classes 转换插件
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);
-
手写转换插件
为了更好的理解我们写的代码,所以我们把抽象语法树都贴出来,对照下前后的区别。
类的 AST
函数的 AST
重写 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);