babel 基础概念

简单来说,做语法转换兼容的,

复杂一点的说,babel可以将我们写的 ES6+ 的Javascript语法转换为向后兼容的语法,以便能够在旧版本的浏览器或者其他环境运行。

babel 生成代码的三个阶段

  • 解析(parse)

    • 输入:源码
    • 输出:AST
  • 转换(transform)
    • 输入:AST
    • 输出:AST(此AST非彼AST,是被不同babel插件处理过的AST)
  • 生成(generate)
    • 输入:AST
    • 输出:转换后的代码

我们平常使用的babel插件是在转换这个阶段生效的,在该阶段babel会去遍历AST的每个节点,我们可以对特定的节点做处理。那这里AST节点是什么呢,请看下面的解释和举例。

AST:抽象语法树

在开始编写插件之前,你需要了解什么是AST。AST是源代码的抽象语法结构的树状表示,它将代码中的结构表示为树中的节点。每个节点代表代码的一部分,例如变量声明、函数调用等。

假设下面这个函数是我们的源码

function square(n) {
return n * n;
}

当我们使用babel进行解析后,会生成如下的AST:

- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n

我们可以看到,第一个节点是FunctionDeclaration,表示我们当前输入的是一个函数的声明,这个节点下是它的标识符(id),参数(params),函数体(body)。

在params和body下,又有下一层级的节点,这个大家可以对照源码看一下

遍历处理AST

从上面的例子我们可以了解到,如果希望对AST进行处理,也就是babel的第二阶段(转换),我们需要递归的对AST这个树结构进行遍历

将上方的AST用js对象表示就是这样

{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}

Visitors

Babel 使用 Visitors 来定义对 AST 节点的具体操作,比如检测特定的节点类型、对节点进行修改、添加新节点等。

Visitors 定义了插件在 AST 上执行的具体操作。一个 Visitor 对象通常包含一组方法,每个方法对应一个特定类型的 AST 节点。这些方法被称为 Visitor 方法,通常以要访问的节点类型名作为属性名。例如:

const visitor = {
Identifier(path) {
// 处理 Identifier 节点
},
BlockStatement(path) {
// 处理 BlockStatement 节点
},
// 其他 Visitor 方法
};

Paths

在 Babel 中,paths 是一个对象,用于在插件中访问和操作 AST 中的节点。每个节点都有一个相应的路径(path),可以通过这个路径对象来访问节点的各种属性和方法。

你可以把 paths 想象成是一条通往 AST 中某个节点的路径,通过这条路径,你可以获取到节点的信息,比如节点的类型、属性、子节点等。这样,你就可以在插件中根据节点的具体情况来进行各种操作,比如修改节点、添加新节点、删除节点等。

比如,你可以在Visitors中使用path打印节点的名称:

const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};

State

在 Babel 插件开发中,state 是一个用于存储插件状态和数据的对象。它可以传递给 Babel 插件中的访问者(Visitors),允许插件在不同的访问者方法之间共享信息和状态。

我们在使用插件的时候,可能会给这个插件传递配置项,这个也可以通过state.opts读取到

// 假设我们在初始化一个插件,并传递了配置项
plugins: [
[myPlugin, { prefix: 'my_' }]
] //
const MyVisitor = {
Identifier(path, state) {
console.log(state.opts); // 打印出来:{ prefix: 'my_' }
}
};

Scope

在 Babel 中,Scope(作用域)是指在 JavaScript 代码中的变量可访问的范围,Babel 中的 Scope 对象用于表示和管理这些作用域。

Scope 在 Babel 中的主要作用是帮助插件进行变量名的解析和处理。Babel 在遍历 AST(抽象语法树)时会构建一个作用域链,每个节点都会有一个对应的 Scope 对象,这个 Scope 对象包含了当前节点所在作用域的信息,比如变量、函数等。

从零到一写一个 babel 插件

项目初始化

mkdir first-babel-plugin
cd first-babel-plugin
npm init -y
npm install --save-dev @babel/core @babel/cli

插件编写

创建 index.js 文件

module.exports = function(babel) {
const { types: t } = babel; return {
visitor: {
CallExpression(path) {
if (
path.node.callee.object &&
path.node.callee.object.name === 'console' &&
path.node.callee.property.name === 'log'
) {
path.remove();
}
}
}
};
};

这个插件定义了一个访问者(visitor),它会遍历AST中的所有调用表达式(CallExpression)。如果这个调用表达式是console.log,它就会被移除。

使用插件

要使用这个插件,你可以在命令行中使用babel命令,并通过--plugins参数指定你的插件。首先,创建一个包含console.log的测试文件,比如test.js:

console.log("Hello, Babel Plugin!");

然后运行:

npx babel test.js --plugins=./index.js

你会看到,输出的代码中不再包含console.log语句。

babel 基础概念 & 从零到一写一个 babel 插件的更多相关文章

  1. 如何写一个jquery插件

      本文总结整理一下如何写一个jquery插件?虽然现今各种mvvm框架异常火爆,但是jquery这个陪伴我们成长,给我们带来很多帮助的优秀的库不应该被我们抛弃,写此文章,作为对以往欠下的笔记的补充, ...

  2. 为PhoneGap写一个android插件

    为PhoneGap写一个android插件,要怎么做? 其实这句话应该反过来说,为android写一个PhoneGap插件,要怎么做? 这里以最简单的Hello World!为例,做个说明: 1.第一 ...

  3. 写一个umi插件 自动生成代码 解放cv的双手

    引言 最近在写一个中台项目,使用的react的umi框架. 各种增删改查.基本是列表页 新建页 详情页这种页面 为了避免不必要的简单重复(主要是想偷懒) 于是想去实现自己的一个代码生成器 探索 首先, ...

  4. 自己写一个 jQuery 插件

    我知道这一天终将会到来,现在,它来了. 需求 开发 SharePoint 的 CSOM 应用时,经常需要在网页上输出一些信息. 这种需求和 alert 的弹窗.F12 的断点查看信息的场景是不一样的: ...

  5. 如何给Ionic写一个cordova插件

    写一个cordova插件 之前由javaWeb转html5开发,由于面临新技术,遂在适应的过程中极为挣扎,不过还好~,这个过程也极为短暂:现如今面临一些较为复杂的需求还会有一丝丝头痛,却没有一开始那么 ...

  6. 基于vue框架手写一个notify插件,实现通知功能

    简单编写一个vue插件,当点击时触发notify插件,dom中出现相应内容并且在相应时间之后清除,我们可以在根组件中设定通知内容和延迟消失时间. 1. 基础知识 我们首先初始化一个vue项目,删除不需 ...

  7. 80行代码教你写一个Webpack插件并发布到npm

    1. 前言 最近在学习 Webpack 相关的原理,以前只知道 Webpack 的配置方法,但并不知道其内部流程,经过一轮的学习,感觉获益良多,为了巩固学习的内容,我决定尝试自己动手写一个插件. 这个 ...

  8. JAVA RPC (六) 之手把手从零教你写一个生产级RPC之client的代理

    首先对于RPC来讲,最主要的无非三点[SERVER IO模型].[序列化协议].[client连接池复用],之前的博客大家应该对thrift有一个大致的了解了,那么我们现在来说一说如何将thrift的 ...

  9. JAVA RPC (七) 手把手从零教你写一个生产级RPC之client请求

    上节说了关于通用请求代理,实际上对spring的bean引用都是通过koalasClientProxy来实现的,那么在代理方法中才是我们实际的发送逻辑,咱们先看一下原生的thrift请求是什么样的. ...

  10. 如何使用jQuery写一个jQuery插件

    jQuery插件其实是前端框架的思维,构成一个框架,个人认为必须满足以下几个基础条件:1. 可重用,2. 兼容性,3. 维护方便,虽说现在有很多比较成熟的前端框架,但是也有部分存在配置麻烦,学习成本大 ...

随机推荐

  1. 一首歌的时间,手把手搭建基于FC的网站

    简介: 不是杰伦的那一首歌啦~ 部署网站 说好不哭 在接触serverless架构之前,我们如果想实现上线一个Web网站,就要在开发前期经过操作很多冗杂但又必须的步骤,不少小白可谓是快速的从入门到退坑 ...

  2. 宜搭5月更新:跨应用数据读写能力升级,AI组件内测开放

    ​简介:表单.权限管理.AI组件等功能上新啦- 本次,我们带来了表单.权限管理.数据管理.平台管理权限.组件等功能的升级. 表单 支持跨应用数据查询 在使用组件数据联动.关联其他表单数据.关联表单组件 ...

  3. 技术干货 | jsAPI 方式下的导航栏的动态化修改

    ​简介: 操作指导:通过 jsAPI 实现导航栏的动态修改. ​ 很多开发同学在接入 H5 容器后都会对容器的导航栏进行深度定制,除了 Native 的定制化之外,还有很多场景是使用到 jsAPI 的 ...

  4. 【CDS技术揭秘系列 01】阿里云CDS-OSS容灾大揭秘

    ​简介: 本文主要阐述 CDS 产品中 OSS 服务在容灾方面的部署形态以及实现的其本原理. 容灾功能可以保证用户一份数据在多个地方存在冗余备份,当某个机房出现极端异常(比如物理损毁)情况下,数据也不 ...

  5. [TP5] ThinkPHP 默认模块和单模块的设置方式

    由于默认是采用多模块的支持,所以多个模块的情况下必须在URL地址中标识当前模块, 如果只有一个模块的话,可以进行模块绑定,方法是应用的入口文件中添加如下代码: // 绑定当前访问到index模块 de ...

  6. [Go] golang 执行 Linux 系统 command

    执行系统 shell 命令示例: fileDir := "files/"out, err := exec.Command("sh", "-c" ...

  7. 2019-11-29-dotnet-代码调试方法

    title author date CreateTime categories dotnet 代码调试方法 lindexi 2019-11-29 8:50:0 +0800 2019-6-5 9:4:4 ...

  8. RT-Thread 堆区大小设置

    一.利用栈区的空间作为堆区 看过我之前的笔记的小伙伴都知道,以前我是通过申请栈区的空间使用的,感兴趣的小伙伴可以看我之前的笔记,RT-Thread移植到stm32. 在board.c文件文件中的代码如 ...

  9. 数字电路中的等效电路和FPGA中的等效电路

    欢迎各位朋友关注"郝旭帅电子设计团队",本微信公众号会定时更新相关技术类资料.软件等等,希望各位朋友都能在本微信公众号获得一些自己想要的"东西". 本篇内容主要 ...

  10. C51笔记-数码管#动态扫描#静态显示#中断#定时器