babel是什么
-
babel是一个用于JavaScript代码转换的工具,可以将现代JavaScript代码转换为向后兼容的代码,以便在旧版浏览器或运行时环境中运行。Babel支持编写自定义插件和预设,以扩展其功能。
-
babel的核心功能包含在@babel/core模块中,它提供了一个transform方法,可以接收一个字符串或者一个抽象语法树(AST)作为输入,并返回一个对象,包含生成的代码,
babel的插件
-
@babel/core babel的编译器核心api都在里面
-
@babel/parser 可以用来转换成AST
-
@babel/types用于AST节点的工具库
-
@babel/traverse 用于对AST结点遍历维护整棵树的状态并且可以移动添加结点
-
@babel/generate 可以用于把AST生成源码同时生成sourcemap
-
等 具体可以去babel阅读查看
关于babelde过程
如何通过babel实现箭头函数转成普通函数
pnpm init //初始化一个项目
pnpm i @babel/core @babel/types -D // 安装babel插件
依赖其他库的实现方式
const babel = require("@babel/core");
const sourceCode = `
const sum =(a,b)=>a+b
`;
const t = require("@babel/types"); //babel的types模块,生成对应的表达式
const arrowFnPlugin =require('@babel/plugin-transform-arrow-functions');
const targetCode =babel.transform(sourceCode, {
plugins:[arrowFnPlugin]
})
console.log(targetCode.code);
//最终输出
const sum = function (a, b) {
return a + b;
};
自己实现
-
首先分析下 “const sum =(a,b)=>a+b” 这代码的AST结构
- 箭头函数没有自己的this 首先看下原时库是怎么实现的
const sourceCode = `
const sum =(a,b)=>{
console.log(this)
return a+b
}
`;
//最终转换成
var _this = this;
const sum = function (a, b) {
console.log(_this);
return a + b;
};
- 还有一个注意点,箭头函数单行是可以省略{}
全部代码
const babel = require("@babel/core");
const sourceCode = `
const sum =(a,b)=>{
console.log(this)
return a+b
}
`;
const t = require("@babel/types"); //babel的types模块,生成对应的表达式
// const arrowFnPlugin =require('@babel/plugin-transform-arrow-functions');
const hoistFunctionEnvironment = (path) => {
//首先要明确箭头函数的this是在定义时决定的
// 向上查找的不是箭头函数的函数或者根节点
const thisEnv = path.findParent((p) => {
return (p.isFunction() && !p.isArrowFunctionExpression()) || p.isProgram();
});
let thisBinding = "_this";
let thisPaths = getScopeInfo(path);
//如果当前的作用域中没有_this,就将this替换成_this
// 如果没有this就不要替换了
if (thisPaths.length > 0) {
if (!thisEnv.scope.hasBinding(thisBinding)) {
thisEnv.scope.push({
id: t.identifier(thisBinding),
init: t.thisExpression(),
});
}
}
thisPaths.forEach((p) => {
p.replaceWith(t.identifier(thisBinding));
});
};
const getScopeInfo = (path) => {
let thisPaths = [];
path.traverse({
//遍历当前路径下的所有节点
ThisExpression(path) {
thisPaths.push(path);
},
});
return thisPaths;
};
const arrowFnPlugin = {
visitor: {
ArrowFunctionExpression(path) {
hoistFunctionEnvironment(path);
// node.type = "FunctionExpression";
const params = path.node.params;
let body = path.node.body;
if (!t.isBlockStatement(body)) {
//如果是当前有代码块了,就不去处理了
//否则就返回一个代码块,返回之前的表达式
body = t.blockStatement([t.returnStatement(body)]);
}
let func = t.functionExpression(null, params, body, false, false);
//将箭头函数替换成函数表达式
path.replaceWith(func);
},
},
};
const targetCode = babel.transform(sourceCode, {
plugins: [arrowFnPlugin],
});
console.log(targetCode.code);
总结
关于过程可能说的很粗狂,可以看上面的完整代码实现,依赖@babel/types,babel/core,很多api的使用都是需要看文档才能完整明白