babel
Babel介绍
Babel是一个JavaScript编译器。
Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
- 语法转换
- 通过 Polyfill 方式在目标环境中添加缺失的特性
- 源码转换
Babel在编译时通过一系列配置实现代码转换。
{
"presets":[...], // 一组Babel插件或者配置组合的模块
"plugins":[...], // 一组用于转换代码的插件
"parserOpts":{
"plugins": [...] // 用于传递给插件的参数
}
}
执行顺序
- 插件在Presets前运行
- 插件顺序从前往后排列
- Presets 顺序是从后往前
Babel处理
Babel的三个主要处理步骤分别是: 解析、转换、生成
解析
解析步骤接收代码并输出 AST,这个步骤氛围两个阶段:词法分析 和 语法分析。
- 词法分析 ⇒ 把字符串形式的代码转换为tokens流
{
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"
}
}
}]
}
}
- 语法分析 ⇒ 把tokens流转换为AST的形式,转换tokens流中的信息为一个AST的表述结构,方便后续操作
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
Babylon 是 Babel的解析器,是在 Acorn项目的基础上进行更新迭代。
import * as babylon from 'babylon'
const code = `function square(n){
return n * n
}`
const ast = babylon.parse(code)
转换
接收AST并对其进行遍历,在此过程中对节点进行添加、更新、删除等操作。这是Babel中最为复杂的过程,同时也是插件介入工作的部分。
babel-traverse 遍历AST维护AST的状态,对其进行替换、移除和添加节点。
import traverse from 'babel-traverse'
traverse(ast,{
enter(path){
...
}
})
- 节点⇒ Node , AST操作的基础对象,各节点存在类似的结构
- 路径⇒ Path, 两个节点之间连接的对象, 提供 节点的访问和增、删、改、查
- 状态⇒ State ,插件状态管理
- 作用域⇒Scope,在代码转换时,注意函数作用域,不能贸然转换破坏其他的代码
生成
把经过转换后的AST结构转换成字符串形式的代码,同时还会创建source map
babel-generator 是Babel的代码生成器,它读取AST并将其转换为代码和sourceMap
import generate from 'babel-generator'
generate(ast,{},code)
babel插件
在转换过程中,通过遍历AST,对其实现节点的添加、更新、删除等操作。而在遍历ast的节点时我们通过一种访问者模式(vistor)的概念,就是定义了一个在遍历过程中获取节点进行处理的方法。
import traverse from 'babel-traverse'
const myPlugin = {
Identifier(){...},
FunctionDeclaration(){...}
}
path.traverse(myPlugin)
插件开发
初始化项目
npm install -g yo generator-babel-plugin
mkdir babel-hello
cd babel-hello
yo babel-plugin
? Plugin Name => 插件名称
? Description => 插件描述
? GitHub username or organization => 开发者
? Author`s Name => 默认开发者
?Author's email => 开发者邮箱
? Key your keywords => 关键词
注意:yo版本3.1.1,否则会出现初始化工程失败
目录结构
- lib 生成的插件目录
- src 插件开发目录
- test 测试目录
- .bablerc 默认babel配置
- package.json
开发介绍
export default function({types:t}){
return {
pre(state){...}, // 插件运行之前
visitor:{...},
post(state){...}, // 插件运行之后
}
}
-
types 作为babel对象中集合多种AST节点类型的操作方法
-
验证节点是否属于某种类型
BinaryExpression(path){ if(t.isIdentifier(path.node.left)){ ... } if(t.isBinaryExpression(path.node)){..} }
-
创建某种类型的节点
BinaryExpression(path){ path.node.left = t.identifier('x') path.node.right = t.expressionStatement(t.stringLiteral('This is a test')) path.replaceWith(t.binaryExpression('**',path.node.left,t.numberLiteral(2))) }
-
创建断言, 断言会直接抛出异常
t.assertBinaryExpression
-
-
visitor 集合你的插件所要包含的操作, 这些操作接收path和state作为参数
visitor:{ FunctionDeclaration(){ ... }, // 函数 => 函数名称 + 函数参数 BinaryExpression(){ ... }, // 表达式 => 操作符 + 变量名 Identifier(){ ... }, // 标识符 ReturnStatement(){ ... }, // 返回值 }
-
path 集合了针对节点的操作
-
节点获取
path.node // 获取节点 => path.node.param/path.node.left path.inList // 判断路径是否有同级节点 path.getSibling(index) 获取同级路径 path.key // 获取路径所在容器的索引 path.container // 获取路径所在容器 path.listKey // 获取容器的key path.findParent // 获取父节点 path.find // 遍历当前节点 path.getFunctionParent // 获取最接近父函数 path.getStatementParent // 向上遍历语法树,直到找到在列表中的父节点路径
-
节点替换
path.replaceWith // 节点替换 path.replaceWith('**',path.node.left,t.numberLiteral(2)) path.replaceWithMultiple // 多节点替换单节点 path.replaceWithSourceString // 源码替换节点
-
节点插入
path.insertBefore // 在节点前插入 path.insertAfter // 在节点后插入 path.unshiftContainer // 插入到容器首部 path.pushContainer // 插入到容器尾部
-
节点域操作
path.scope.hasBinding // 检查本地变量是否被绑定 path.scope.generateUidIdentifier // 创建域名uid path.scope.rename // 重命名绑定及引用
-
节点其他
path.remove // 删除节点 path.skip // 跳过当前路径下节点的遍历 path.stop // 停止遍历 path.buildCodeFrameError // 抛出错误
-
-
State, 传递插件配置的参数
{ plugins: [ ["my-plugin", { "option1": true, "option2": false }] ] } { visitor:{ FunctionDeclaration(path,state){ console.log(state.opts) } } }
GitHub - zjwgank/babel-demo: This is a demo for babel plugin
引用
babel-handbook/plugin-handbook.md at master · jamiebuilds/babel-handbook