@babel/types从入门到放弃

背景

我一直在思考一个问题,为什么线上出现问题时总是难以找到原因呢?每一次都是遇到问题之后,开始排查问题,开始根据报错信息直接去翻看源码(我相信一般的公司应该不会把 sourcemap 打一份放到内网里面),于是翻来覆去一顿操作之后找到了问题,然后紧急发布解决问题;然后每一次出现线上故障都是这样的操作,非常之低效;

既然要快速定位线上问题,那就必须对线上代码有所了解;可以说大部分同学都只了解自己所写代码,而并不了解打包之后代码成了什么样子(由于浏览器兼容性问题,打包之后代码可能会面目全非)。我不想成为他们之中的一员,所以一直在看打包之后的代码,以及编译器 babel,即使现在新出了 swc、esbuild,babel 仍然是主流。最近我在看装饰器的编译时犯了难,这里面用到了@babel/types 这个库,当我去查看文档时,它的文档是这样的:

没有示例,也没有解释这个表达式的含义,只给了一些参数,总之这个文档太水了。现在我要让你们忘记这个文档,不要再看这个文档,回想一下第一次学 js 的场景。

js 数据类型

第一天学的一定是 js 的数据类型:

  • Null
  • Undefined
  • Boolean
  • Number
  • String
  • Symbol
  • Object

用 babel 如何生成这些类型的值呢?请大家先准备好 node 环境,安装如下依赖:

  • @babel/generator
  • @babel/types或者@babel/core(我这里用的@babel/core)

大功告成之后,先定义一个函数,将 ast 转换为代码,方便后续打印值;

js 复制代码
const generate = require("@babel/generator").default;

function log(node) {
  const code = generate(node).code;
  console.log("code:", code);
}

创建简单类型值

js 复制代码
const { parse, types } = require("@babel/core");

const _null = types.nullLiteral();
log(_null); // code: null

const boolean = types.booleanLiteral(true);
log(boolean); // code: true

const number = types.numericLiteral(42);
log(number); // code: 42

const string = types.stringLiteral("Hello, World!");
log(string); // code: Hello, World!

总结一下简单数据类型如何创建 ast:

  • Null:types.nullLiteral()
  • Boolean:types.booleanLiteral(true)
  • Number:types.numericLiteral(42)
  • String:types.stringLiteral("Hello, World!")
  • Undefined和 Symbol 类型都没有办法创建

创建Array

object 类型,又分为 Array、Object、Function等等,数组类型最为简单,先实现一个简单的数组吧!实现数据使用:arrayExpression

js 复制代码
const string = types.stringLiteral("Hello, World!");
const number = types.numericLiteral(42);
const boolean = types.booleanLiteral(true);
const array = types.arrayExpression([string, number, boolean]);

log(array); // code: ["Hello, World!",42,true]

创建 Object

对象类型比较复杂,首先要执行objectExpression创建对象,然后objectProperty声明属性:

js 复制代码
const object = types.objectExpression([
  types.objectProperty(types.stringLiteral("name"), types.stringLiteral("John")),
  types.objectProperty(types.stringLiteral("age"), types.numericLiteral(30)),
]);
log(object); // {name:"John",age:30}

创建函数

函数声明包含函数名、参数、函数体,另外一个函数还需要区分是否为 generator 函数,是否为 async 函数,创建函数需要借助functionExpression,有五个参数:

  • id: types.Identifier, 函数名
  • params: (types.Identifier | types.RestElement | types.Pattern)[], 函数参数
  • body: types.BlockStatement, 函数体
  • generator?: boolean, 是否为generator函数
  • async?: boolean,是否为异步函数

函数名和参数名都属于标识符,函数体属于块声明,需要blockStatement创建,执行函数需要callExpression创建,表达式则为expressionStatement

接下来实现一个打印其参数的函数:

js 复制代码
const func = types.functionExpression(
  types.identifier("func"),
  [types.identifier("params")],
  types.blockStatement([
    types.expressionStatement(types.callExpression(types.identifier("console.log"), [types.identifier("params")])),
  ]),
  false,
  false
);
log(func); 
// code:function func(params) {
//   console.log(params);
// }

总结一下创建复杂数据类型都用到了哪些方法:

  • Array:types.arrayExpression()
  • Object:types.objectExpression()、types.objectProperty()
  • Function:types.functionExpression()、types.blockStatement()、types.expressionStatement()、types.callExpression()

js 语法

变量声明variableDeclaration

js 复制代码
const res = types.variableDeclaration("const", [
  types.variableDeclarator(types.identifier("age"), types.numericLiteral(42)),
]);
log(res); // code: const age = 42;

赋值表达式assignmentExpression

赋值表达式首先需要传入操作符,然后就是左值和右值,但是这里如果没有声明则是全局变量

js 复制代码
log(types.assignmentExpression("=", types.identifier("age"), types.numericLiteral(42)));
// code: age = 42

序列表达式sequenceExpression

序列表达式其实就是逗号操作符,比如:(1,2),利用sequenceExpression创建

js 复制代码
log(types.sequenceExpression([types.numericLiteral(1), types.numericLiteral(2)]));
// 2

条件语句ifStatement

js 复制代码
const ifstatement = types.ifStatement(
  types.identifier("name"),
  types.blockStatement([
    types.expressionStatement(
      types.assignmentExpression("=", types.identifier("name.this"), types.stringLiteral("John"))
    ),
  ])
);
log(ifstatement);
// code: if (name) {
//   name.this = "John";
// }

循环语句forStatement

js 复制代码
const forstatement = types.forStatement(
  types.variableDeclaration("let", [types.variableDeclarator(types.identifier("i"), types.numericLiteral(0))]),
  types.binaryExpression(">", types.identifier("i"), types.numericLiteral(10)),
  types.assignmentExpression(
    "=",
    types.identifier("i"),
    types.binaryExpression("+", types.identifier("i"), types.numericLiteral(1))
  ),
  types.blockStatement([
    types.expressionStatement(types.callExpression(types.identifier("console.log"), [types.identifier("i")])),
  ])
);
log(forstatement);
// code: for (let i = 0; i > 10; i = i + 1) {
//   console.log(i);
// }

到这里为止就实现了一些常用数据类型和语法,为以后写 babel 插件打下基础

相关推荐
一个处女座的程序猿O(∩_∩)O5 分钟前
完成第一个 Vue3.2 项目后,这是我的技术总结
前端·vue.js
mubeibeinv6 分钟前
项目搭建+图片(添加+图片)
java·服务器·前端
逆旅行天涯12 分钟前
【Threejs】从零开始(六)--GUI调试开发3D效果
前端·javascript·3d
m0_7482552633 分钟前
easyExcel导出大数据量EXCEL文件,前端实现进度条或者遮罩层
前端·excel
长风清留扬1 小时前
小程序毕业设计-音乐播放器+源码(可播放)下载即用
javascript·小程序·毕业设计·课程设计·毕设·音乐播放器
web147862107231 小时前
C# .Net Web 路由相关配置
前端·c#·.net
m0_748247801 小时前
Flutter Intl包使用指南:实现国际化和本地化
前端·javascript·flutter
飞的肖1 小时前
前端使用 Element Plus架构vue3.0实现图片拖拉拽,后等比压缩,上传到Spring Boot后端
前端·spring boot·架构
青灯文案11 小时前
前端 HTTP 请求由 Nginx 反向代理和 API 网关到后端服务的流程
前端·nginx·http
m0_748254881 小时前
DataX3.0+DataX-Web部署分布式可视化ETL系统
前端·分布式·etl