亮神知识库 亮神知识库
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)

亮神

前程明亮,未来可期
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)
  • babel

    • 摘要
    • Babel与Ts
    • 代码压缩原理
    • Babel原理
      • babel流程
      • babel的架构
      • 访问者模式(了解)
      • 面试
  • webpack基础

  • webpack深入

  • Vite

  • TypeScript

  • 打包工具
  • babel
0zcl
2021-12-18
目录

Babel原理

# babel流程

  1. 解析(parsing),得到AST语法树
    • 词法解析(Lexical Analysis): 得到一个包含词法信息的 <code>Tokens </code>数组。每个Token元素包含了语法片段、位置信息、以及类型信息
    • 语法解析(Syntactic Analysis): 把 <code>Tokens </code>数组转换为 <code>抽象语法树AST </code>
  2. 转换(transform),得到新的AST语法树: 转换阶段会对AST进行遍历,可对AST进行增删改查。Babel插件都是在这个阶段工作的
  3. 生成(generate),生成JS代码,同时会生成Source Map

词法解析生成的Tokens数组

词法Tokens

<code>console.log('hello world')</code>对应的AST语法树。<code>Program </code>、<code>CallExpression </code>、<code>Identifier </code> 这些都是节点的类型,每个节点都是一个有意义的语法单元。 这些节点类型定义了一些属性来描述节点的信息。插件开发者会利用 astexplorer (opens new window) 来审查解析后的AST树

AST

# babel的架构

Babel 和 Webpack 为了适应复杂的定制需求和频繁的功能变化,都使用了 微内核 (opens new window) 的架构风格。也就是说它们的核心非常小,大部分功能都是通过 <code>插件扩展 </code>实现的

babel

  1. <code>@babel/core </code>是Babel的核心。负责加载插件;调用 <code>Parser </code>进行解析,生成AST树;调用 <code>Traverser </code>遍历AST;生成代码,包括SourceMap转换和源代码生成
  2. Parser(@babel/parser): 将源代码解析成AST
  3. Traverser(@babel/traverser): 实现了 <code>访问者模式 </code>,对AST进行遍历,<code>转换插件 </code>会通过它获取感兴趣的AST节点,对节点进行操作
  4. Generator(@babel/generator): 将AST转换为源代码,支持SourceMap
  5. 转换插件:Babel仓库将转换插件划分为两种(只是命名上的区别)
    • <code>@babel/plugin-transform-*</code>: 普通的转换插件
    • <code>@babel/plugin-proposal-*</code>: 还在’提议阶段’(非正式)的语言特性
  6. 插件预调:插件集合,方便对插件进行管理和使用。<code>@babel/preset-env </code>智能预设,允许你使用最新的JavaScript。无需关心语法转换的细节
  7. <code>@babel/types </code>: AST 节点构造器和断言. 插件开发时使用很频繁
  8. @babel/node: 可直接支行需要Babel处理的JS文件
  9. @babel/cli: Cli工具

# 访问者模式(了解)

访问者模式平时基本没接触,可以看访问者模式 (opens new window)加深理解。个人理解:<code>访问者(Visitor)定义好visit方法,参数为被访问的对象 </code>。想象一下,Babel 有那么多插件,如果每个插件自己去遍历AST,对不同的节点进行不同的操作,维护自己的状态。这样子不仅低效,它们的逻辑分散在各处,会让整个系统变得难以理解和调试, 最后插件之间关系就纠缠不清,乱成一锅粥。

所以转换器操作 AST 一般都是使用访问器模式,由这个访问者(Visitor)来

  1. 进行统一的遍历操作
  2. 提供节点的操作方法
  3. 响应式维护节点之间的关系
  4. 访问者(即插件)只需要定义自己感兴趣的节点类型,当访问者访问到对应节点时,就调用插件的访问(visit)方法

访问者会以深度优先的顺序, 或者说递归地对 AST 进行遍历,其调用顺序如下图所示: 绿线表示进入该节点,红线表示离开该节点。

traverser

当访问者进入一个节点时就会调用 enter(进入) 方法,反之离开该节点时会调用 exit(离开) 方法。 一般情况下,插件不会直接使用enter方法,只会关注少数几个节点类型,所以具体访问者也可以这样声明访问方法:

const traverse = require('@babel/traverse').default
const babel = require('@babel/core')
const generate = require('babel-generator').default
const code = `function hello(v) {
  console.log('hello' + v + '!')
}`
const ast = babel.parseSync(code)

traverse(ast, {
  Identifier(path) {
    console.log(`enter Identifier`)
  },
  CallExpression(path) {
    console.log(`enter CallExpression`)
  },
  BinaryExpression: {
    enter(path) {
      console.log('enter binaryExpression')
    },
    exit(path) {
      console.log('exit binaryExpression')
    },
  }
})
const output = generate(ast)
/*
enter Identifier
enter Identifier
enter CallExpression
enter Identifier
enter Identifier
enter binaryExpression
enter binaryExpression
enter Identifier
exit binaryExpression
exit binaryExpression

{
  code: 'function hello(v) {\n  console.log(\'hello\' + v + \'!\');\n}',
  map: null,
  rawMappings: null
}
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 将console.log('hello' + v + '!')语句替换为return "hello" + v;, 下图是遍历的过程:
traverse(ast, {
  ExpressionStatement(path) {
    // 将 `console.log('hello' + v + '!')` 替换为 `return ‘hello’ + v`
    const rtn = t.returnStatement(t.binaryExpression('+', t.stringLiteral('hello'), t.identifier('v')))
    path.replaceWith(rtn)
  },
})
1
2
3
4
5
6
7
8

replace

# 面试

问:说说你对Babel的了解?

答: Babel的原理:是解析源代码,经过词法分析和语法分析,得到AST树;对AST树进行遍历转换,得到新的AST树;通过新的AST生成新的源代码。

Babel的架构:是微内核架构,和webpack, postcss类型。babel的核心非常小,大部分功能是通过插件扩展的。

Babel插件有很多,比如:@babel/core:babel的核心、@babel/parser: 将源代码转换成AST、@babel/traverser: 遍历AST、@babel/generator: 将AST转换成源代码、@babel/cli: babel的cli工具、@babel/preset-env:智能预设插件集合。。。。等等

问:写过Babel插件吗?说下原理

答: 写过一个去除debugger语法 (opens new window)的babel插件。

原理:

  1. 解析源代码,经过词法分析和语法分析,得到AST树
  2. 对AST树进行遍历转换,得到新的AST树;
  3. 通过新的AST生成新的源代码。 插件是在第二步,对AST树做增删查改的处理。第二步中,使用了访问者模式,AST树相当于被访问者,而插件访问AST,做处理,所以是访问者。访问者实现一个visit接口,参数为被访问的对象(即AST节点)
module.exports = function() {
  return {
    visitor: {
      DebuggerStatement(path) {
        path.remove()
      }
    }
  }
}
1
2
3
4
5
6
7
8
9

参考: 深入浅出 Babel 上篇:架构和原理 + 实战 (opens new window) astexplorer (opens new window) Babel插件手册 (opens new window) 访问者模式 (opens new window)

编辑 (opens new window)
上次更新: 2025/07/20, 08:30:18
代码压缩原理
基础

← 代码压缩原理 基础→

最近更新
01
2024年
07-20
02
2023年
07-20
03
2022年
07-20
更多文章>
Theme by Vdoing | Copyright © 2025-2025 亮神 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式