官网:babeljs.io
官方 Presets 说明:babeljs.io/docs/preset…
Babel 8 迁移:babeljs.io/docs/v8-mig…
文档定位:面向完全没用过 Babel 的读者;读完后应能说出「Babel 在流水线里干什么、常见 npm 包各自负责什么、Babel 7 和 8 配置差在哪」。
一、Babel 是什么?解决什么问题?
1.1 一句话
Babel 是一个 JavaScript「编译器」:把你写的现代 JS(或 JSX、TypeScript 等)转成目标环境能运行的 JS。
它不是打包工具(不负责合并文件、拆 chunk),也不是浏览器。它只做一件事:改代码本身。
1.2 为什么需要它?
| 你写的代码 | 浏览器/旧 Node 可能的情况 |
|---|---|
const、箭头函数、async/await | 老环境语法不支持 |
?. 可选链、?? 空值合并 | 需要语法转换 |
Promise、Array.prototype.at | 语法有了,但内置 API 没有 → 需要 polyfill |
JSX <App /> | 浏览器不认识 JSX → 要转成 React.createElement 或自动运行时 |
语法转换和 polyfill 是两件不同的事,后面会反复提到。
1.3 和 Webpack / Vite 的关系
你的源码 → [ Babel:改语法、插 polyfill ] → 仍是多个 .js 文件
↓
[ Webpack/Vite:打包、压缩、拆包 ] → 浏览器下载的 bundle
很多项目里 Babel 嵌在构建工具里(例如 babel-loader),但概念上它们是上下游关系。
2026 年的现实:Vite 默认用 esbuild 做转译,往往不装 Babel也能开发;只有需要特定插件、旧浏览器深度兼容、或 JSX/TS 特殊处理时,才显式加 Babel。本文仍讲 Babel,因为它是理解「现代 JS 如何落到旧环境」的经典路径。
二、运行原理:从源码到输出
2.1 核心流水线(必记)
Babel 内部大致分四步,由 @babel/core 串联:
flowchart LR
A[源代码字符串] --> B[Parse 解析]
B --> C[AST 抽象语法树]
C --> D[Transform 转换]
D --> E[Generate 生成]
E --> F[输出代码字符串]
| 阶段 | 做什么 | 类比 |
|---|---|---|
| Parse | 把字符串解析成树形结构 AST | 把文章拆成「主谓宾」结构 |
| Transform | 插件/预设遍历 AST,改节点 | 按规则改写法,意思不变 |
| Generate | 把 AST 打印回 JS 字符串 | 把改好的结构写回正文 |
Plugin(插件) 负责具体某一种改写(例如:把箭头函数改成普通函数)。
Preset(预设) 是一组插件的打包套餐(例如 @babel/preset-env = 一堆「按目标浏览器启用」的语法插件)。
2.2 配置如何参与?
你写的 babel.config.json 不会「魔法执行」,而是告诉 @babel/core:
- 用哪些 presets(从后往前执行,见下文)
- 用哪些 plugins
- targets(要支持哪些浏览器/Node 版本)
每次编译一个文件时,@babel/core 加载配置 → 跑完所有转换 → 输出字符串。
2.3 Preset 的执行顺序(易踩坑)
官方约定:presets 数组从右到左执行(与 plugins 相同)。
{ "presets": ["a", "b", "c"] }
实际顺序:c → b → a。
历史原因是为了兼容老配置里先写 es2015 再写 stage-0 的写法。
2.4 一张图串起来
┌─────────────────────────────────────────────────────────────┐
│ babel.config.json │
│ presets: [@babel/preset-env, @babel/preset-react] │
│ plugins: [babel-plugin-polyfill-corejs3, ...] │
│ targets: { chrome: 80 } 或 browserslist │
└──────────────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ @babel/core │
│ parse → traverse(AST) + 各 plugin 改节点 → generate │
└──────────────────────────┬──────────────────────────────────┘
▼
转换后的 .js 文本
▼
(可选)webpack + babel-loader 再打包
三、核心概念速查
| 概念 | 含义 |
|---|---|
| 语法转换(Syntax transform) | 改语言结构,如 class → 函数、?? → 三元表达式 |
| Polyfill | 补上运行时缺失的 API(如 Promise、Object.fromEntries),常在全局或模块里注入 |
| Helper | Babel 转换时插入的小函数(如 _extends),可用 @babel/runtime 避免每文件重复 |
| targets | 要支持的环境;决定「哪些转换可以不做」 |
| browserslist | 用一串查询语句描述目标浏览器,Babel 可复用 |
四、常见依赖包是干什么的?
下面按「你在 package.json 里最常见到的名字」说明。加粗的是几乎每个项目都会遇到的。
4.1 核心与命令行
| 包名 | 类型 | 作用 |
|---|---|---|
@babel/core | 必装(被别的包依赖) | Babel 引擎:解析、转换、生成;babel-loader 最终调用的就是它 |
@babel/cli | 可选 dev | 命令行 babel src -d lib,不经过 Webpack 时直接转目录 |
@babel/parser | 通常不直接装 | 解析器;作为 core 依赖存在 |
@babel/traverse / @babel/types / @babel/generator | 一般不直接装 | 遍历 AST、节点类型定义、代码生成;写自定义插件时才常接触 |
4.2 官方 Presets(套餐)
来自 官方 Presets 列表:
| 包名 | 作用 |
|---|---|
@babel/preset-env | 根据 targets 自动启用 ES2015+ 等语法插件;Babel 7 还可配 useBuiltIns 管 polyfill(Babel 8 已移除) |
@babel/preset-react | 把 JSX 转成 JS;需关注 runtime: "classic" 或 "automatic"(React 17+ 自动运行时) |
@babel/preset-typescript | 剥掉 TS 类型,保留 JS;不做类型检查(类型检查仍是 tsc) |
@babel/preset-flow | 去掉 Flow 类型注解 |
Stage-X preset(
stage-0等)自 Babel 7 起已废弃,应改用具体@babel/plugin-proposal-*/@babel/plugin-transform-*。
4.3 常用 Plugins(单点能力)
| 包名 | 作用 |
|---|---|
@babel/plugin-transform-runtime | 把 helper 抽成 @babel/runtime 的 import,减小体积;库开发常用;Babel 8 起不再用它的 corejs 选项管 polyfill |
@babel/plugin-proposal-* / @babel/plugin-transform-* | 单项语法;Stage 4 后多改名为 transform-* |
babel-plugin-polyfill-corejs3 | 新一代 polyfill 注入方式,可替代 preset-env 的 useBuiltIns;Babel 8 推荐/必需 |
4.4 运行时与 Polyfill 相关
| 包名 | 安装位置 | 作用 |
|---|---|---|
core-js | 视方案而定 | 实现 ES 标准库 API 的 polyfill 本体(Promise、Map 等) |
@babel/runtime | 生产依赖(库) | 存放 Babel 注入的 helper 函数实现,避免重复 |
@babel/runtime-corejs3 | 生产依赖(库,旧方案) | 配合 transform-runtime 的 corejs 做不污染全局的 polyfill;Babel 8 迁移到 polyfill 插件 |
regenerator-runtime | 视需要 | 支持 async/await 转译后的运行时(preset-env 场景常间接用到) |
@babel/polyfill | 已废弃 | 老项目可能见过,勿在新项目使用 |
4.5 与构建工具集成
| 包名 | 作用 |
|---|---|
babel-loader | Webpack 规则里:test: /\.jsx?$/ 时用 Babel 处理;options 可写配置,但更推荐独立的 babel.config.json |
| (无独立包) | Vite 通过 @vitejs/plugin-react 等决定是否走 Babel |
4.6 辅助:browserslist
| 包名/文件 | 作用 |
|---|---|
browserslist | 在 package.json 或 .browserslistrc 里写 > 0.5%, not dead;@babel/preset-env 默认会读它作为 targets |
五、两种 Polyfill 思路(读懂配置的关键)
很多初学者晕在「到底要不要 core-js、要不要 useBuiltIns」——记住下面两种场景即可。
5.1 做网站 / 业务应用(允许改全局)
目标:bundle 里带上当前浏览器缺的 API,可以改 Array.prototype 等全局对象。
- Babel 7 常见写法:
preset-env+useBuiltIns: "usage"+corejs: 3 - Babel 7/8 更现代写法:顶层
targets+babel-plugin-polyfill-corejs3+method: "usage-global"
5.2 做 npm 库 / 组件库(不能污染使用方全局)
目标:只给自己包的代码补 API,不要改用户项目的全局环境。
- Babel 7 常见写法:
@babel/plugin-transform-runtime+@babel/runtime-corejs3(且不要同时开useBuiltIns) - Babel 8:
transform-runtime(仅 helper)+babel-plugin-polyfill-corejs3+method: "usage-pure"
5.3 互斥提醒
不要在同一配置里既开 useBuiltIns(或 usage-global)又开 transform-runtime 的 polyfill,容易重复注入、体积暴涨。Webpack 示例里也只选一条路。
六、Babel 7:版本说明与推荐配置
6.1 版本现状(截至 2026 年初)
| 项 | 说明 |
|---|---|
| 当前稳定线 | 7.29.x(7.x 最后一个 minor,维护模式) |
| 默认安装 | npm i -D @babel/core 得到的是 7.x |
| 特点 | 仍支持 preset-env 的 useBuiltIns / corejs;文档与生态最全 |
6.2 最小可运行示例
安装(应用项目示例):
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install core-js@3
babel.config.json:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "defaults",
"useBuiltIns": "usage",
"corejs": "3.41"
}
]
]
}
含义简述:
targets:支持哪些浏览器(也可用.browserslistrc)useBuiltIns: "usage":只对你源码里用到的 API 注入 polyfillcorejs:指定 polyfill 实现版本,需安装core-js@3
命令行试跑:
npx babel src/index.js -o dist/index.js
6.3 带 React 的 Babel 7 配置
npm install --save-dev @babel/preset-react
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": "3.41",
"modules": false
}
],
[
"@babel/preset-react",
{
"runtime": "automatic"
}
]
]
}
modules: false:保留import/export,交给 Webpack/Vite 做 tree-shakingruntime: "automatic":React 17+ 无需每文件import React
6.4 Babel 7 + Webpack(babel-loader)
webpack.config.js 片段:
module.exports = {
module: {
rules: [
{
test: /\.m?jsx?$/,
exclude: /node_modules/,
use: "babel-loader"
}
]
}
};
配置放在根目录 babel.config.json,不要在 loader 里再写一套冲突的 useBuiltIns + transform-runtime。
6.5 Babel 7 库作者配置(不污染全局)
npm install --save-dev @babel/plugin-transform-runtime
npm install @babel/runtime-corejs3
{
"presets": [
["@babel/preset-env", { "modules": false }]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3
}
]
]
}
6.6 Babel 7 向「新 polyfill 插件」过渡(可选)
即使还在 7.x,也可以不用 useBuiltIns,改用官方推荐的 polyfill 插件,方便以后升 8:
npm install --save-dev babel-plugin-polyfill-corejs3
npm install core-js@3
{
"targets": { "chrome": "80" },
"presets": ["@babel/preset-env"],
"plugins": [
[
"babel-plugin-polyfill-corejs3",
{
"method": "usage-global",
"version": "3.41"
}
]
]
}
| method | 对应 Babel 7 老写法 |
|---|---|
usage-global | useBuiltIns: "usage" |
entry-global | useBuiltIns: "entry" |
usage-pure | transform-runtime + corejs |
七、Babel 8:版本说明与推荐配置
7.1 版本现状(截至 2026 年初)
| 项 | 说明 |
|---|---|
| 状态 | RC 阶段(如 8.0.0-rc.6),正式版未 stable |
| 安装 | npm install @babel/core@next 等 |
| 定位 | 清理历史包袱:ESM-only、移除 useBuiltIns、默认行为更接近现代工程 |
7.2 与 Babel 7 的主要差异(必知)
| 主题 | Babel 7 | Babel 8 |
|---|---|---|
| 包格式 | CommonJS + ESM | 仅 ESM |
| Node 版本 | 较宽 | ^22.18.0 或 ≥24.11.0(跑 Babel 本身的环境) |
preset-env 默认 targets | 类似 >= 0%(全兼容) | defaults(更现代) |
| Polyfill | useBuiltIns + corejs | 移除;必须用 babel-plugin-polyfill-corejs3 |
transform-runtime 的 corejs | 支持 | 移除 |
@babel/preset-react 默认 runtime | classic | automatic |
loose / spec(preset-env) | 常用 | 迁移到顶层 assumptions |
| 迁移文档 | — | v8-migration |
7.3 Babel 8 应用项目推荐配置
npm install --save-dev @babel/core@next @babel/preset-env@next @babel/preset-react@next
npm install --save-dev babel-plugin-polyfill-corejs3
npm install core-js@3
{
"targets": "defaults",
"presets": [
[
"@babel/preset-env",
{
"bugfixes": true,
"modules": false
}
],
[
"@babel/preset-react",
{
"runtime": "automatic"
}
]
],
"plugins": [
[
"babel-plugin-polyfill-corejs3",
{
"method": "usage-global",
"version": "3.41"
}
]
]
}
要点:
targets写在配置文件顶层(不要只写在 preset-env 里),与 polyfill 插件共用。- 开启
bugfixes: true(官方迁移清单建议)。 - 不要再写
useBuiltIns、corejs(在 preset-env 里)。
7.4 Babel 8 库作者配置
{
"targets": "defaults",
"presets": [
["@babel/preset-env", { "modules": false, "bugfixes": true }]
],
"plugins": [
"@babel/plugin-transform-runtime",
[
"babel-plugin-polyfill-corejs3",
{
"method": "usage-pure",
"version": "3.41"
}
]
]
}
生产依赖:
npm install @babel/runtime
(polyfill 以模块形式注入,由 usage-pure + core-js 配合完成。)
7.5 从 Babel 7 升级到 8 的检查清单
- Node 升到 22.18+ 或 24.11+。
- 把
targets提到配置顶层;确认与 browserslist 一致。 - 删除
useBuiltIns、corejs(preset-env)、transform-runtime的corejs。 - 安装并配置
babel-plugin-polyfill-corejs3。 - 显式设置
@babel/preset-react的runtime(避免和旧项目行为不一致)。 - 将
proposal-*插件改为对应的transform-*(若仍在使用)。 - 在 CI 用
@babel/core@next跑一遍构建与测试。
八、配置文件放在哪?
| 文件 | 作用范围 |
|---|---|
babel.config.json(或 .js / Babel 8 的 .ts) | 整个项目,含 monorepo 根;推荐 |
.babelrc / .babelrc.json | 当前 package 目录及其子目录 |
package.json 的 "babel" 字段 | 等同于小型 .babelrc |
Babel 8 支持 babel.config.ts(TypeScript 写配置)。
九、常见问题(FAQ)
Q:Babel 会检查 TypeScript 类型吗?
A:不会。preset-typescript 只删类型;类型检查用 tsc 或 IDE。
Q:为什么还要 Babel,Vite 不是很快吗?
A:Vite 开发时常用 esbuild 转译;需要特定插件、老旧浏览器、或统一和后端构建链时,仍可能引入 Babel。
Q:node_modules 会被 Babel 转吗?
A:默认 Webpack 规则 exclude: /node_modules/;除非你有意编译某个 ESM 包。
Q:useBuiltIns: "entry" 和 "usage" 区别?
A:entry 在入口根据 targets 引入一整包 polyfill,体积大;usage 按源码实际用到的 API 按需引入,更省(Babel 7)。
Q:我只支持 Chrome 最新,还要 polyfill 吗?
A:把 targets 设得很新(或 browserslist 只写现代浏览器),Babel 会少转或少插 polyfill,体积自然变小。
十、延伸阅读与仓库内文档
| 资源 | 链接 |
|---|---|
| Presets 概念与顺序 | babeljs.io/docs/preset… |
| preset-env 全部选项 | babeljs.io/docs/babel-… |
| polyfill 插件迁移表 | github.com/babel/babel… |
| Babel 8 迁移 | babeljs.io/docs/v8-mig… |
附录:依赖关系一张表
┌─────────────────┐
│ @babel/core │
└────────┬────────┘
┌───────────────────┼───────────────────┐
▼ ▼ ▼
@babel/preset-env @babel/preset-react plugins...
│ │
│ └── 处理 JSX
│
├── 语法 plugins(按 targets 启用)
│
└── Babel7: useBuiltIns → core-js
Babel8: babel-plugin-polyfill-corejs3 → core-js
@babel/plugin-transform-runtime → @babel/runtime(helper)
babel-loader(Webpack)──调用──► @babel/core
读完本文后,建议动手:用 Babel 7 配一个最小 babel.config.json,npx babel 转一个含 ?. 和 Promise.allSettled 的文件,再改 targets 对比输出差异——比死记配置项更有效。