babel
babel
是一个JavaScript
编译器,用于代码转换:
- 语法转换:将
ES6
、ES7
等新版本的JavaScript
代码转换为ES5
代码 - 代码转换:将
TypeScript
、JSX
等代码转换为JavaScript
代码 - 代码压缩:将
JavaScript
代码压缩为最小化的代码
核心库
@babel/core
@babel/core
是babel
的核心库,负责babel
转换的整个流程,它具备以下关键功能:
- 解析源代码以生成
AST
- 遍历
AST
并对节点进行转换 - 将转换后的
AST
重新生成代码
transform
是@babel/core
对外暴露的核心方法,用于将源代码转换为目标代码,并且可以接收一个配置对象作为参数,用于指定转换的插件和预设
@babel/cli
、babel-loader
等工具库都是基于@babel/core
的transform
方法实现的
js
const babel = require("@babel/core");
const code = `[1, 2, 3].map(x => x + 1)`;
const transformResult = babel.transform(code, {
plugins: [require("@babel/plugin-transform-arrow-functions")],
});
console.log(transformResult.code); // [1, 2, 3].map(x => x + 1);;
@babel/core
会自动读取项目根目录下创建.babelrc
、babel.config.js
、babel.config.json
文件等,用于指定转换的插件和预设
json
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"ie": "11",
"edge": "17"
}
}
]
],
"plugins": ["@babel/plugin-transform-arrow-functions"]
}
@babel/cli
@babel/cli
可以在命令行中直接执行babel
的转换操作,它是babel
的命令行工具,它可以在命令行中直接执行babel
的转换操作
@babel/cli
的核心转换还是借助@babel/core
进行实现的,所有在使用@babel/cli
之前,需要先安装@babel/core
bash
# 安装 babel-cli 工具
pnpm install -D @babel/cli @babel/core
并且可以接受参数,指定输入和输出文件
bash
# 将 src 目录下的代码转换为 dist 目录下的代码
npx babel src -D dist
@babel/cli
还可以指定转换的插件和预设
bash
# 指定转换的插件和预设
npx babel src -d dist --plugins=@babel/plugin-transform-arrow-functions --presets=@babel/preset-env
当然,因为@babel/cli
是基于@babel/core
,所以在项目根目录下创建了.babelrc
、babel.config.js
、babel.config.json
文件等,@babel/cli
也会自动读取这些文件,用于指定转换的插件和预设
json
{
// 指定转换的预设
"presets": [
[
"@babel/preset-env",
{
"targets": {
"ie": "11",
"edge": "17"
}
}
]
],
// 指定转换的插件
"plugins": ["@babel/plugin-transform-arrow-functions"]
}
bash
# 会自动读取项目根目录下创建的 .babelrc、babel.config.js、babel.config.json 文件等,用于指定转换的插件和预设
npx babel src -d dist
@babel/plugin-transform-*
@babel/plugin-transform-*
是babel
的插件,它可以对AST
进行转换,从而实现对代码的转换
babel 提供了很多插件,比如@babel/plugin-transform-arrow-functions
、@babel/plugin-transform-block-scoping
、@babel/plugin-transform-class-properties
等 通过一系列的插件组合,转换出我们想要的代码,每个插件都有自己的转换任务
@babel/plugin-transform-arrow-functions
@babel/plugin-transform-arrow-functions
是babel
的插件,它可以将箭头函数转换为普通函数
项目根目录下创建babel.config.json
文件
json
{
"plugins": ["@babel/plugin-transform-arrow-functions"]
}
在src
目录下创建index.js
文件,并在index.js
文件中编写需要转代码
js
[1, 2, 3].map((x) => x + 1);
在命令行中执行转换操作,将src
目录下的代码转换为dist
目录下的代码,按照根目录babel.config.json
文件中的配置进行转换
bash
npx babel src -d dist
@babel/plugin-transform-runtime
@babel/plugin-transform-runtime
是babel
的插件,它可以将babel
的运行时依赖抽离出来,避免全局污染
@babel/plugin-transform-runtime
内部实现也是依赖core-js
,但是使用的位置是来自于@babel/runtime-corejs3
js
[1, 2, 3].includes(2);
Promise.resolve(1);
json
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3, // 指定 core-js 的版本
"helpers": true, // 启用辅助函数的重用
"regenerator": true // 启用对生成器函数的支持
}
]
]
}
生成的代码如下:
js
var _context;
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
import _Promise from "@babel/runtime-corejs3/core-js-stable/promise";
_includesInstanceProperty(_context = [1, 2, 3]).call(_context, 2);
_Promise.resolve(1);
@babel/polyfill
@babel/plugin-transform-*
可以提供代码语法转换,但是不能提供一些新的 API 转换,比如Promise
、Set
、Map
、Array.from
、Array.of
、Object.assign
等,这些新的 API 是在ES6
中引入的,但是在ES5
中是没有的,所以需要使用@babel/polyfill
来提供这些新的 API 补丁
在babel7
中,@babel/polyfill
已经被弃用,推荐使用core-js
来提供这些新的 API 补丁 core-js
能够实现按需加载,只加载需要的 API 补丁,从而减少代码的体积
js
require("core-js/modules/es.array.includes.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.promise.js");
@babel/preset-*
@babel/preset-*
是babel
的预设,它可以根据浏览器环境自动选择需要转换的插件
@babel/preset-env
@babel/preset-env
是babel
的预设,它可以根据浏览器环境自动选择需要转换的插件和预设
json
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"ie": "11",
"edge": "17"
}
}
]
]
}
@babel/preset-env
还可以按需加载polyfill
json
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": "3"
}
]
]
}
useBuiltIns
提供三个可选值:
false
:不自动使用任何polyfill
entry
:根据浏览器环境自动引入全部polyfill
,但需要在入口文件中引入core-js
usage
:根据代码中已经使用的API
按需引入
转换流程
解析源代码以生成 AST
@babel/core
集成了@babel/parser
功能,用于解析代码生成AST
js
const parser = require("@babel/parser");
const code = `[1, 2, 3].map(x => x + 1)`;
// 解析代码生成 AST
const ast = parser.parse(code);
// 打印 AST 抽象语法树
console.log(ast);
遍历 AST
并对节点进行转换
@babel/core
集成了@babel/traverse
功能,用于遍历AST
,再通过@babel/plugin-*
、@babel/preset-*
等插件对节点进行转换
@babel/traverse
的traverse
方法会遍历AST
的所有节点,每个节点会调用的需要转换规则,从而实现对代码的转换
js
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const code = `[1, 2, 3].map(x => x + 1)`;
// 解析代码生成 AST
const ast = parser.parse(code);
// 遍历 AST 并对节点进行转换
traverse(ast, {
ArrowFunctionExpression(path) {
// 对箭头函数进行转换
path.node.type = "FunctionExpression";
},
});
// 打印转换后的 AST 抽象语法树
console.log(ast);
转换后的 AST
重新生成代码
@babel/core
集成了@babel/generator
功能,用于将转换后的AST
重新生成代码
js
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;
const code = `[1, 2, 3].map(x => x + 1)`;
// 解析代码生成 AST
const ast = parser.parse(code);
// 遍历 AST 并对节点进行转换
traverse(ast, {
ArrowFunctionExpression(path) {
// 对箭头函数进行转换
path.node.type = "FunctionExpression";
},
});
// 转换后的 AST 重新生成代码
const { code: generatedCode } = generator(ast);
// 打印生成的代码
console.log(generatedCode);
其他问题
@babel/polyfill
、core-js
的区别
@babel/polyfill
是将所有的polyfill
都引入到全局作用域中,从而实现全局的polyfill
功能,但是这在babel7
中已经被弃用,推荐使用core-js
来实现core-js
能够实现按需加载,只加载需要的polyfill
,从而减少代码的体积
@babel/plugin-transform-runtime
、@babel/preset-env
的区别
都是解决如何按需加载polyfill
的问题,但是倾向的方向不一样
@babel/plugin-transform-runtime
用于在转换过程中重用辅助函数,避免全局污染,并按需引入polyfills
@babel/preset-env
用于根据目标环境自动选择需要的polyfills
,并且可以配置按需加载的polyfills
,但都是全局引入
当同时使用@babel/plugin-transform-runtime
和@babel/preset-env
时,@babel/plugin-transform-runtime
会替代@babel/preset-env
中的polyfills
js
[1,2,3].includes(2)
Promise.resolve(1)
json
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"ie": "11"
},
"modules": false,
"useBuiltIns": "usage", // 或 "usage",根据需要选择
"corejs": 3 // 指定 core-js 的版本
}
]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3, // 指定 core-js 的版本
"helpers": true, // 启用辅助函数的重用
"regenerator": true // 启用对生成器函数的支持
}
]
]
}
如果使用@babel/plugin-transform-runtime
配合@babel/preset-env
,打包之后代码:
js
var _context;
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
import _Promise from "@babel/runtime-corejs3/core-js-stable/promise";
import "core-js";
_includesInstanceProperty(_context = [1, 2, 3]).call(_context, 2);
_Promise.resolve(1);