什么是 Babel?
官方的解释 Babel 是一个 JavaScript 编译器,用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前版本和旧版本的浏览器或其他环境中。简单来说 Babel 的工作就是:
- 语法转换
- 通过 Polyfill 的方式在目标环境中添加缺失的特性
- JS 源码转换
Babel的原理和流程
- Parser(@babel/parser):词法解析(Lexical Analysis),词法解析器(Tokenizer)江代码分割成(Tokens)语法片段数组
- Parser(@babel/parser): 语法解析(Syntactic Analysis):这个阶段语法解析器(Parser)会把Tokens转换为抽象语法树(Abstract Syntax Tree,AST)。语法插件(@babel/plugin-syntax-*)参与
- Traverser(@babel/traverse):遍历(访问者模式)AST,并应用转换器
- Traverser(@babel/traverse):transformer,AST转换器,增删改节点,(@babel/plugin-transform-*)插件参与
- Generator(@babel/generator):代码生成,将AST转化为字符串形式新代码,同时这个阶段还会生成Source Map。
功能图
包结构
- @babel/core核心包,内置一下包
- 代码解析器Parser(@babel/parser)
- AST遍历器Traverser(@babel/traverse)
- 代码生成器Generator(@babel/generator)
- 语法插件(@babel/plugin-syntax-*)
- 转换插件(@babel/plugin-transform-*)
- 预案插件(@babel/plugin-proposal-*)
- 插件预定义集合(@babel/presets-*)presets-env
- 模板引擎@babel/template更方便操作AST
- @babel/types: AST 节点构造器和断言. 插件开发时使用很频繁
- @babel/helper-*: 一些辅助器,用于辅助插件开发,例如简化AST操作
- @babel/helper: 辅助代码,单纯的语法转换可能无法让代码运行起来,比如低版本浏览器无法识别class关键字,这时候需要添加辅助代码,对class进行模拟。
工具
- @babel/node: Node.js CLI, 通过它直接运行需要 Babel 处理的JavaScript文件
- @babel/register: Patch NodeJs 的require方法,支持导入需要Babel处理的JavaScript模块
- @babel/cli: CLI工具,命令式操作babel
访问者模式
- 转换器操作 AST 一般都是使用访问器模式,由这个访问者(Visitor)来
- 进行统一的遍历操作,
- 提供节点的操作方法,
- 响应式维护节点之间的关系;
- 而插件(设计模式中称为‘具体访问者’)只需要定义自己感兴趣的节点类型,当访问者访问到对应节点时,就调用插件的访问(visit)方法。
写个简单插件
- 对AST进行操作,实现按需加载代码转换
- import { Button } from 'antd'前
- import Button from "antd/lib/Button"后
//插件函数
const pluginImport = function({ types: t }) {
return {
visitor: {//返回访问者对象
ImportDeclaration(path) {
const { node: { specifiers, source } } = path;
if (!t.isImportDefaultSpecifier(specifiers[0])) { // 对 specifiers 进行判断,是否默认倒入
const newImport = specifiers.map(specifier => (
t.importDeclaration(
[t.ImportDefaultSpecifier(specifier.local)],
t.stringLiteral(`${source.value}/lib/${specifier.local.name}`)
)
))
path.replaceWithMultiple(newImport)
}
}
}
}
}
//测试使用
import * as babel from '@babel/core';
const c = `import { Button } from 'antd'`;
const { code } = babel.transform(c, {
plugins: [pluginImport]
})
console.log(code); // import Button from "antd/lib/Button";