# 前言
本文旨在记录 Babel
学习过程,记录 Babel
各个包之间的关系及其作用。
文章基于 babel 7+
# 什么是 Babel
Babel
为了解决各个运行平台对于es语言的差异性而带来的一款工具。
# Babel 是一个 javascript complier
Babel是一个工具链,主要用于在当前和较旧的浏览器或环境中将ECMAScript 2015+代码转换为JavaScript的向后兼容版本。 以下是Babel可以为您做的主要事情:
- 语法转换 transform syntax
- 目标环境中缺少的Polyfill功能(通过@babel/polyfill)
- 源代码转换(codemods)
# Babel 概念
# 汇总
概念 | 包含库 | 说明 |
---|---|---|
core | @babel/core | babel compiler 核心编译器,用于解析,转换代码。 |
cli | @babel/cli | babel 内置的命令行工具,可用于使用命令行工具编译代码。 |
plugins | Transform Plugins @babel/plugin-transform-runtime @babel/plugin-transform-react-jsx ..... | 应用于代码转换的插件,内部调用 @babel/core 转换代码, 插件是观察则模式的对象,只对观察的语法实施转换 |
presets | @babel/preset-env @babel/preset-flow @babel/preset-react @babel/preset-typescript | 一个预设是一组 plugin 的集合 , 可以定义自己的预设。 |
cli | @babel/plugin-transform-runtime | 一个通过重复利用 Babel 注入的帮助程序 @babel/runtime 的插件 |
# @babel/core
@babel/core
是一个基于 在 node
中运行的代码编译器,无论是通过 @babel/cli
工具还是 webpack的 babel
插件转译,内部都是通过 node
调用 @babel/core
相关功能来实现代码转译。是一个开发依赖包。
npm install --save-dev @babel/core
@babel/core
内部实现了 transform
, tranformFile
, transformFromAst
方法。都实现了 callback
方式调用, 同步方式,和异步的 promise
方式调用接口,
- transform
babel.transform(code: string, options?: Object, callback: Function)
- transformSync
const {code, map, ast} = babel.transformSync(code: string, options?: Object)
- transformAsync
babel.transformAsync(code, options).then(result => {
const {code, map, ast} = result
});
2
3
- transformFile
babel.transformFile(filename, options, callback)
- transformFileSync
const {code, map, ast} = babel.transformFileSync(filename, options)
- transformFileAsync
babel.transformFileAsync(filename, options).then(result => {
const {code, map, ast} = result
});
2
3
- transformFromAst
const sourceCode = "if (true) return;";
const parsedAst = babel.parse(sourceCode, { parserOpts: { allowReturnOutsideFunction: true } });
babel.transformFromAst(parsedAst, sourceCode, options, function(err, result) {
const { code, map, ast } = result;
});
2
3
4
5
6
- transformFromAstSync
const sourceCode = "if (true) return;";
const parsedAst = babel.parse(sourceCode, { parserOpts: { allowReturnOutsideFunction: true } });
const { code, map, ast } = babel.transformFromAstSync(parsedAst, sourceCode, options);
2
3
4
- transformFromAstAsync
const sourceCode = "if (true) return;";
babel.parseAsync(sourceCode, { parserOpts: { allowReturnOutsideFunction: true } })
.then(parsedAst => {
return babel.transformFromAstAsync(parsedAst, sourceCode, options);
})
.then(({ code, map, ast }) => {
// ...
});
2
3
4
5
6
7
8
9
# @babel/cli
@babel/cli
是 Babel
官方提供的 cli
脚手架工具,用于在命令行中执行编译命令,输出编译后的代码。
不推荐在全局安装 @babel/cli
命令行工具,原因:
- 同一台计算机可能运行不同版本
Babel
的项目,安装在项目内可以分别更新维护。 - 项目内安装能降低项目对全局环境的依赖,更好的可移植性支持。
npm install --save-dev @babel/core @babel/cli
创建测试文件 test.js
const aa = () => { const bb = [1, 2]; console.log(...bb); };
const {x, y} = obj;
const [a, b, ...rest] = arr;
2
3
运行命令
npx babel test.js
输出
const aa = () => {
const bb = [1, 2];
console.log(...bb);
};
const {
x,
y
} = obj;
const [a, b, ...rest] = arr;
2
3
4
5
6
7
8
9
10
可以看到并没有任何转译。
这事由于只安装了脚手架工具和核心包 @babel/core
,并没有安装具体的实施转换对应代码的 plugins
我们这里希望解析箭头函数的语法,所以需要安装解析箭头函数的插件 @babel/plugin-transform-arrow-functions
npm i -D @babel/plugin-transform-arrow-functions
# 安装解构语法支持
npm i -D @babel/plugin-transform-destructuring
2
3
安装了解析转换的 plugin
之后,我们还需要配置使用插件。
对于
cli
工具,我们可以使用命令行方式指定使用plugin
具体使用可以查看命令行帮助npx babel --help
使用本地配置文件
.babelrc
或者.babelrc.json
文件配置,优先级低于命令行配置
所以我们可以
npx babel test.js --plugins @babel/plugin-transform-arrow-functions,@babel/plugin-transform-destructuring
输出
const aa = function () {
const bb = [1, 2];
console.log(...bb);
};
const _obj = obj,
x = _obj.x,
y = _obj.y;
const _arr = arr,
_arr2 = _toArray(_arr),
a = _arr2[0],
b = _arr2[1],
rest = _arr2.slice(2);
2
3
4
5
6
7
8
9
10
11
12
13
14
或者在根目录下创建 .babelrc 文件
{
"presets": [],
"plugins": [
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-destructuring"
]
}
2
3
4
5
6
7
运行命令
npx babel test.js
输出和 cli 方式配置一致。
# @babel/plugin
plugin
使用详见上一小节。
plugin
是直接作用于代码的语法转换工具,它定义了 指定语法的转换规则,内部调用 @babel/core
的 transform
方法实现语法转换。 plugin
关注转换逻辑, @babel/core
实现转换操作的关系。
plugin
基于访问者 Visitor
模式,将作用域代码块解析出的 AST
抽象语法树 的某一节点的操作分离出来,在不改变 AST
结构的前提下,转换关注的 代码块。
# plugin 开发
插件是基于 访问者模式 ,先将代码块解析成 AST 抽象语法树, 然后遍历树节点, 找到自己要访问的节点,然后基于特定的规则,实现对这一块代码的 AST 重新组装, 最后调用 AST 生成代码的方法,将转换后的 AST 再次转换成代码块。从而实现代码的转译。
Babel
使用一个基于ESTree
并修改过的AST
。@babel/types
可以根据 options 创建 AST ,@babel/types 文档
一段简单的实例代码,实现箭头函数的转换
//babel核心库,用来实现核心的转换引擎
let babel= require('@babel/core');
//可以实现类型判断,生成AST节点
let types = require('@babel/types');
let code = `let sum = (a,b)=>a+b`; // let sum = function (a,b){return a+b}
// 这个访问者可以对特定的类型的节点进行处理
let visitor = {
ArrowFunctionExpression(path){
let params = path.node.params;
let blockStatement = types.blockStatement([
types.returnStatement(path.node.body)
]);
let func = types.functionExpression(null, params, blockStatement, false, false);
path.replaceWith(func);
}
}
let arrayPlugin = {visitor}
//babel内部先先把代码转成AST,然后进行遍历,
console.log(code)
let result = babel.transform(code,{
plugins:[
arrayPlugin
]
})
console.log(result.code);
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
output
let sum = (a,b)=>a+b
let sum = function (a, b) {
return a + b;
};
2
3
4
# @babel/preset
通过使用或创建一个 preset 即可轻松使用一组插件。
# 官方preset
-
@babel/preset-env
是一个智能预设,它使您可以使用最新的JavaScript,而无需微观管理目标环境所需的语法转换(以及可选的浏览器polyfill)。 这都使您的生活更轻松,JavaScript包更小!需要说明的是,@babel/preset-env 会根据你配置的目标环境,生成插件列表来编译。对于基于浏览器或 Electron 的项目,官方推荐使用 .browserslistrc 文件来指定目标环境。默认情况下,如果你没有在
Babel
配置文件中(如.babelrc
)设置targets
或ignoreBrowserslistConfig
,@babel/preset-env
会使用browserslist
配置源。如果你不是要兼容所有的浏览器和环境,推荐你指定目标环境,这样你的编译代码能够保持最小。
例如,仅包括浏览器市场份额超过0.25%的用户所需的
polyfill
和代码转换(忽略没有安全更新的浏览器,如IE10
和BlackBerry)
://.browserslistrc > 0.25% not dead
1
2
3查看 browserslist 的更多配置
-
如果您使用
Flow
(JavaScript代码的静态类型检查器),则建议使用此预设。 它包含以下插件: -@babel/plugin-transform-flow-strip-types
# @babel/plugin-transform-runtime
一个插件,可重复使用Babel注入的帮助程序代码以节省代码大小。
npm install --save-dev @babel/plugin-transform-runtime
# 帮助程序是作为运行时需要的,作为 dependencies 安装
npm install --save @babel/runtime
2
3
// .babelrc
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"helpers": true,
"regenerator": true,
"useESModules": false,
}
]
]
}
2
3
4
5
6
7
8
9
10
11
12
13