babel 中文网址 www.babeljs.cn/docs/
github 用户手册
github 插件手册
1,babel定义
把es5+、jsx、ts等转为es5,以便浏览器运行
- 语法转换
- 通过polyfill 模块添加缺失的特性 (polyfill是shim的一种,shim是将不同 api封装成一种,比如 jQuery的 $.ajax封装了XMLHttpRequest和 IE用ActiveXObject方式创建xhr对象)
- 源码转换(codemods)
2,babel配置
覆盖或增加默认的配置文件,与默认的配置merge
-
babel.config.json
Babel 将自动在这个根目录中搜索一个babel.config.json文件 -
babelrc.json 从正在编译的“文件名”开始的目录结构搜索, 允许您为包的子部分创建独立的配置
-
以babel:{} 形式添加到 package.json 文件中
内容格式
{ "presets": [...], "plugins": [...] }
presets:可以被看作是一组 Babel 插件,官方提供的预设如下:
- @babel/preset-env for compiling ES2015+ syntax
- @babel/preset-typescript for TypeScript
- @babel/preset-react for React
- @babel/preset-flow for Flow plugins:数组格式,从后往前执行,常用见 www.babeljs.cn/docs/plugin… 支持自己扩展
ps: json文件后缀可扩展 .js, .cjsand .mjs
3,工具包
一个简单demo:
import { parse } from '@babel/parser';
import traverse from '@babel/traverse';
import generate from '@babel/generator';
const code = 'const n = 1';
// parse the code -> ast
const ast = parse(code);
// transform the ast
traverse(ast, {
enter(path) {
// in this example change all the variable `n` to `x`
if (path.isIdentifier({ name: 'n' })) {
path.node.name = 'x';
}
},
});
// generate code <- ast
const output = generate(ast, code);
console.log(output.code); // 'const x = 1;'
- babylon: babylon.parse(code, {})
- @babel/parser: babelParser.parse(code, [options]),点击体验ast
- @babel/traverse: 遍历转换nodes
- @babel/generator: 将AST转成js代码
- @babel/core: 一系列transform和parser函数,babel.transform(code: string, options?: Object, callback: Function)
- @babel/template: 辅助函数,将字符串形式代码构建ast树
- @babel/code-frame
- @babel/runtime
- @babel/types: 用于 AST 节点的 Lodash 式工具库
4,es6转es5
- parser 解析 :解析位ast抽象语法树
- transform 转化:通过配置的plugin和preset标准将ast转为新的ast
- generator 生成:将转化后的ast生成新的code
5,自定义myPlugin
{
"plugins": [
["babel-plugin-myPlugin",{ "key1": "value1", "method": "fn" }],
"@babel/plugin-transform-runtime"
]
}
export default function({ types,template,generator,traverse,env }) {
return {
//用于在一个树状结构中获取具体节点的方法
visitor: {
//每当在树中遇见一个 `Identifier` 的时候会调用
Identifier(path) {
const name = path.node.name;
// 反转名称顺序
path.node.name = name .split("") .reverse() .join("");
},
//var a = 1;
VariableDeclaration(path) {},
FunctionDeclaration(path) {},
//
ASTNodeTypeHere(path, state) {},
//
Program(path, state) {},
//foo === bar
BinaryExpression(path) {}
},
};
}
path属性如下,还包含添加、更新、移动和删除节点有关的其他很多方法
{
"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
}
- 替换
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
)
- 插入
path.insertBefore
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')))
- 删除
path.remove()
- 替换父节点
path.parentPath.replaceWith
- 提升变量声明至父级作用域
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
- 递归
path.traverse(MyVisitor)
- 检查本地变量是否被绑定
if (path.scope.hasOwnBinding("n")) {...}
6、调试自定义plugin方式
我们自定义babel代码的位置为./plugin/index
6.1 通过babel命令
babel --plugin ./plugin/index src/demo.js --out-dir lib
6.2 @babel/core
const babel = require('@babel/core');
const myplugin = require('./plugin/index');
const code = `
const sum = (a, b) => a + b;
`;
babel.transform(code, {
plugins: [myplugin]
}).code;
写在后面
因为大学读的机械专业,最近才看了大学教程《编译原理》,真的有一种相见恨晚的感觉。觉得对于前端开发来说,这本书真的值得一读。目前读的还比较浅,只是对编译有了自己的认识而已,我最喜欢的一句话,“书读百遍,其义自现”,每次重读,都会有不一样的感受。