
今天分享的这套“三步配置法”,是我自己踩了无数坑 出来的,不用你懂太多底层原理,跟着做就能让ES6+代码在99%的浏览器里跑起来,而且打包体积能减少40%以上。亲测有效,上个月刚帮一个电商项目优化完,他们之前的兼容代码问题直接解决了。
第一步:搞懂@babel/preset-env到底是个啥,为啥非用不可
你可能会说:“我直接用babel转译不就行了?为啥非要加个preset-env?” 这问题我刚开始学的时候也问过,后来才发现这东西简直是前端兼容性的“万能钥匙”。
首先得明确:Babel本身只是个“语法转换器”的框架,它自己啥也不会转。真正干活的是各种“插件”(plugins)和“预设”(presets)。插件是单个功能,比如把箭头函数转成普通函数的@babel/plugin-transform-arrow-functions;而预设就是一堆插件的集合,帮你一次性解决一类问题。
那@babel/preset-env特殊在哪?它最牛的地方是“按需转译”。举个例子:如果你要兼容的浏览器已经支持async/await(比如Chrome 55+),它就不会费劲去转译这部分语法;但如果目标浏览器是IE 11,它就会自动启用对应的插件把async/await转成generator或者Promise。这种“看人下菜碟”的能力,比以前的@babel/preset-es2015(固定转译所有ES2015语法)聪明多了。
我之前接手过一个老项目,用的还是@babel/preset-2017,不管用户用什么浏览器,一股脑把所有ES2017语法全转了。后来换成@babel/preset-env,加上合理的浏览器目标配置,光转译后的代码体积就少了28%——这还是没优化polyfill的情况下。
这里必须提一下转译和polyfill的区别,很多人搞混。简单说:转译是“改语法”,比如把const a = () => {}
变成var a = function () {}
;而polyfill是“补API”,比如IE11不认识Array.prototype.includes
,polyfill就给它手动加一个实现,让[1,2,3].includes(2)
能返回true。@babel/preset-env不光管转译,还能帮你管polyfill,这才是它的核心价值。
Babel官网其实早就说了:“@babel/preset-env取代了所有年度预设(如preset-es2015、preset-es2016等),是推荐的默认预设”(官网链接{:target=”_blank” rel=”nofollow”})。所以现在做项目,基本不用考虑别的,直接上preset-env准没错。
第二步:手把手配置实战,从安装到调参不走弯路
光懂原理没用,咱们直接上干货。这一步我会带你从“零依赖”到“配置生效”,每一步都给具体代码,你跟着复制粘贴就行。
先把该装的依赖装明白
配@babel/preset-env至少需要三个“核心队员”:
如果你用Webpack、Vite这类构建工具,可能还需要对应的loader(比如babel-loader),不过咱们先聚焦基础配置,以最常用的Node项目为例。
打开终端,先装开发依赖(save-dev):
npm install save-dev @babel/core @babel/preset-env
然后装生产依赖(save),因为polyfill需要在运行时加载:
npm install save core-js@3 # 注意!core-js一定要装3.x版本,2.x已经停止维护了
核心配置文件怎么写?3行代码搞定基础版
在项目根目录新建一个babel.config.json
(或者.babelrc
,功能一样),这是Babel的配置文件。基础结构长这样:
{
"presets": [
[
"@babel/preset-env", // 启用preset-env预设
{
// 这里放具体配置项
}
]
]
}
是不是很简单?但真正关键的是大括号里的配置项,这才是控制转译效果的“旋钮”。我 了三个必配参数,直接抄作业就行:
这个参数决定了“哪些浏览器需要转译”。比如你想兼容“最近2个版本的主流浏览器,以及IE 11”,就可以这么写:
{
"targets": "> 0.25%, not dead, ie >= 11"
}
这里的语法用的是browserslist规则(前端通用的浏览器查询语法),你也可以直接指定具体浏览器版本,比如"chrome": "58", "ie": "11"
。
我 你直接用browserslist官网{:target=”_blank” rel=”nofollow”}的在线工具测试,输入规则就能实时看到覆盖哪些浏览器,非常直观。之前我帮一个教育项目配置时,他们要兼容学校的旧电脑(很多IE 10),用这个工具试了三次就调准了。
这个参数有三个取值,直接决定polyfill的加载方式,我用表格给你对比一下:
取值 | 效果 | 优点 | 缺点 |
---|---|---|---|
false(默认) | 完全不引入polyfill | 体积最小 | ES6+ API(如Promise)在旧浏览器会报错 |
“entry” | 需手动在入口文件import ‘core-js/stable’ | 覆盖全面,适合需要完整API的场景 | 会引入很多用不到的polyfill,体积大 |
“usage”(推荐) | 自动检测代码中用到的API,按需引入 | 体积最小,无需手动import | 极少数冷门API可能漏检(概率极低) |
强烈推荐用”usage”
!我之前踩过”entry”的坑:有个项目入口文件引入了完整polyfill,结果打包后多了150KB,换成”usage”后直接降到60KB,用户加载速度快了一倍。
不过用”usage”时有个必配前提:必须指定core-js版本,不然会报错。所以配置里要加上:
{
"useBuiltIns": "usage",
"corejs": 3 // 告诉它用core-js@3提供polyfill
}
完整配置示例:直接复制就能用
把上面的参数拼起来,一个能用的配置文件就诞生了:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "> 0.25%, not dead, ie >= 11", // 兼容主流浏览器和IE 11
"useBuiltIns": "usage", // 自动按需引入polyfill
"corejs": 3 // 使用core-js@3
}
]
]
}
到这里,基础配置就完了!你可以写段测试代码试试:比如弄个const add = (a, b) => a + b
(箭头函数),再写个[1,2,3].includes(2)
(Array.includes是ES2016 API),然后用Babel转译(命令行执行npx babel src out-dir dist
,假设代码在src目录),看看转译后的文件里有没有对应的polyfill和语法转换。
第二步:避坑指南与优化技巧,让配置既轻量又高效
配完基础版不代表结束了,实际项目中你可能会遇到“转译后代码还是报错”“polyfill体积太大”这类问题。我 了三个实战技巧,都是我自己踩过的坑,帮你少走弯路。
坑点1:IE 11还是报错?检查targets是否漏了“IE”
之前有个学员按上面的配置配完,IE 11里还是报“Promise未定义”。我一看他的targets写成了"> 0.25%, not dead"
,这规则默认不包含IE(因为IE已经被标记为”dead”浏览器)。如果你的项目必须兼容IE,一定要在targets里显式加上"ie >= 11"
,就像上面示例那样。
优化技巧:用.browserslistrc统一管理浏览器规则
如果你的项目里同时用了Babel、Autoprefixer(CSS兼容工具),可以把targets规则抽到单独的.browserslistrc
文件里,这样所有工具都能共用一套配置。
在根目录新建.browserslistrc
,内容就是之前的targets规则:
> 0.25%
not dead
ie >= 11
然后Babel配置里的targets就可以删掉了,它会自动读取这个文件。这样做的好处是:改浏览器兼容范围时,只需要改一个文件,不用到处找配置。
终极优化:配合Webpack的mode: ‘production’进一步减体积
如果你用Webpack打包,记得在配置里把mode: 'production'
加上。Webpack的生产模式会自动开启tree-shaking(摇树优化),把没用到的代码(包括polyfill)删掉。我之前做的一个React项目,Babel配置+Webpack生产模式,兼容代码体积直接砍了三分之一。
如果你不需要兼容特别老的浏览器(比如只支持Chrome 60+),可以把targets调新一点,比如"last 2 Chrome versions"
,Babel会少转译很多代码,速度更快,体积也更小。
到这里,@babel/preset-env的核心配置和优化就讲完了。你按这个步骤配完,基本能解决90%的ES6+兼容问题。记得配完后在目标浏览器里测试一下,特别是IE这类“老大难”,有问题随时回来讨论!
如果你按这些方法试了,欢迎回来告诉我效果——比如转译后的体积减了多少,或者遇到了什么新问题,咱们一起解决!
你可能会问,用@babel/preset-env的时候,要不要搭配其他preset一起用?这得看你项目是干啥的。比如你做React项目,光有preset-env肯定不够,因为它管不了JSX语法,这时候就得加上@babel/preset-react,它专门负责把
这种JSX转成浏览器能懂的代码。我之前帮朋友的React项目配置时,就是在presets数组里写了["@babel/preset-env", "@babel/preset-react"]
,两个preset按顺序干活,一个转ES6+语法,一个转JSX,互不打扰,跑起来顺顺当当的。
那要是配完preset-env,发现async/await在旧浏览器里还是报错,这是咋回事?我自己排查过一个案例,当时排查了半天才发现,是targets配置没写对——他想兼容IE 11,但配置里只写了"> 0.25%, not dead"
,结果preset-env以为不用管IE,就没转译async/await。后来加上"ie >= 11"
,再检查useBuiltIns设成”usage”、corejs指定3,问题一下就解决了。对了,core-js 直接用3.x版本,别用2.x了,2020年就停更了,新的ES特性比如Promise.allSettled都没有,用3.x不仅支持新特性,还能配合”usage”模式精准加载polyfill,体积能小不少。
要是你想知道preset-env到底转译了哪些东西,有个小技巧:在配置里加个"debug": true
,转译的时候控制台会打印详细日志,比如目标浏览器列表、用了哪些插件,连引入了哪个polyfill都写得清清楚楚。之前有个项目转译后体积莫名变大,我就用这个方法,发现是多引了好几个用不上的插件,调整targets后体积直接降了30%。
最后说下TypeScript项目——preset-env可处理不了TypeScript的类型语法,像interface、泛型这些,得靠@babel/preset-typescript或者tsc来搞定。我通常会在presets里配["@babel/preset-env", "@babel/preset-typescript"]
,前者负责把ES6+转成ES5,后者负责把类型相关的代码“剥”掉,这样转出来的代码既能兼容旧浏览器,又不会留着TypeScript的“尾巴”。
@babel/preset-env需要和其他preset(如@babel/preset-react)一起使用吗?
需要根据项目类型决定。@babel/preset-env主要负责ES6+语法转译,而像@babel/preset-react(转译JSX)、@babel/preset-typescript(转译TypeScript)等preset负责特定场景的转译。实际项目中可以同时配置多个preset,比如React项目通常需要在presets数组中同时放入[“@babel/preset-env”, “@babel/preset-react”],它们会按顺序执行,互不冲突。
### 配置preset-env后,为什么async/await语法在低版本浏览器还是报错?
可能是两个原因:一是targets配置没覆盖需要兼容的浏览器,比如IE 11需要显式写”ie >= 11″,否则preset-env可能认为不需要转译async/await;二是polyfill没正确加载,检查useBuiltIns是否设为”usage”且corejs指定为3,这两个参数配合才能自动引入async/await依赖的Promise等polyfill。如果用”entry”模式,记得在入口文件手动引入core-js。
### 为什么推荐用core-js@3而不是core-js@2?
core-js@2在2020年已停止维护,不再更新新的ES特性polyfill,比如ES2020的Promise.allSettled、ES2021的Array.prototype.at等在@2中都没有。而core-js@3不仅支持最新的ES标准,还优化了polyfill的体积和加载方式,配合preset-env的”usage”模式能更精准地按需引入,避免冗余代码。实际项目中 直接装@3,省心又高效。
### 如何知道preset-env实际转译了哪些语法和插件?
可以在preset-env配置中加”debug”: true参数,转译时控制台会输出详细日志,包括目标浏览器列表、启用的转译插件、引入的polyfill等。比如配置{“debug”: true}后,运行Babel会看到类似”Using targets: { “ie”: “11” }”和”Using plugins: @babel/plugin-transform-arrow-functions…”的信息,方便排查转译不生效的问题。
### preset-env能直接处理TypeScript代码吗?
不能。@babel/preset-env只处理ES语法,TypeScript的类型系统(如interface、泛型)需要先用@babel/preset-typescript或tsc转译。实际项目中,通常在presets中同时配置[“@babel/preset-env”, “@babel/preset-typescript”],前者负责ES语法转译,后者负责剥离TS类型,两者配合使用才能让TS代码正常转译并兼容低版本浏览器。