
最近在Code Review里,我看到一个新同学在一个vite.config.js
里,习惯性地加上了@vitejs/plugin-legacy
,用来支持旧版浏览器。
我问他:"我们的目标用户里,真的还有人用那些浏览器吗?"
他愣了一下,说:"不知道,但加上总比不加好吧?万一呢?"
这个回答,让我陷入了沉思。"加上总比不加好",这句听起来非常正确的话,真的是对的吗?
要做好兼容性,这句话,就像一句咒语,刻在了我们这代前端工程师的骨子里。从当年大战IE6,到后来ES6普及时,为各种新语法打补丁,polyfill
和babel
就是我们的救命稻草。在入口文件顶部写下一行import 'core-js/stable'
,仿佛是一种兼容标准。
但现在都2025年了,IE早已入土为安,主流浏览器都已现代化。我越来越觉得,盲目地、无差别地为项目引入全量polyfill
,已经从一个最佳实践,变成了一种技术债。
过度追求兼容性,让我们付出了哪些代价?
我们先来算一笔账。当我们无脑地引入一个完整的polyfill
方案时,我们付出的代价是什么?
打包体积的代价
这是最直接的代价,最终由我们的用户来买单。
我随手在一个Vite + Vue 3的项目里,
javascirpt
import 'core-js/stable'
import 'regenerator-runtime/runtime'
只是import 'core-js/stable'
,然后用rollup-plugin-visualizer
分析一下打包体积:

一个完整的core-js
,会给你的JS包增加几百KB的大小,而且占用整个项目78%的代码!!!这些代码,对于你项目中99%的、使用现代浏览器的用户来说,是完全用不上、不会被执行的死代码。他们却要为这部分代码,付出实实在在的流量和加载时间。
运行时性能的代价
Polyfill不只是下载下来就完事了,它还需要在浏览器里被解析和执行。虽然这个开销对于单个polyfill来说很小,但当一个庞大的polyfill集合在你的应用启动时就开始注入和执行,这无疑会增加主线程的负担,对首次可交互时间(TTI)和总阻塞时间(TBT)产生负面影响。
都2025年了,我们到底在兼容什么?
既然代价如此之大,那我们回头看看,我们如此大费周章,到底是在兼容什么?
首先,我们必须明确一点:IE已经死了!

在2025年,任何还在要求兼容IE的项目,要么是预算给够的古董维护项目,要么就应该重新评估它的商业价值。对于绝大多数面向公众的互联网产品,我们可以、也应该,大胆地和IE说再见。
其次,主流浏览器都是常青的。
Chrome, Firefox, Edge都具备自动更新能力,这意味着你绝大部分的用户,使用的都是最新或次新的浏览器版本,它们对ES2020+的特性支持都非常好。

那么,我们现在做兼容,主要目标是谁?
答案是:那些无法更新操作系统的、旧款设备上的浏览器。
所以,问题的关键就变成了:你的用户里,到底有多少人在用这些过时的浏览器?
根据 Statcounter的数据 ,全球浏览器市场份额👇:

Chrome 仍然是全球用户的首选,其次是 Safari,成为第二受欢迎的选择。Edge、Firefox 和 Samsung Internet 也占有相当大的份额,而 Opera 和其他小众浏览器则占到只有 5% 的市场份额。
作为技术组长,我要求我们团队做任何关于兼容性的决策前,必须拿数据说话。打开你们自己项目的统计后台看一看,那个小于1%的others,真的值得我们让99%的用户去承担性能代价吗?
我的一些建议,Polyfill使用策略
我不是在鼓吹完全放弃兼容性,而是主张一种更智能、更精准、代价更小的策略。
明确定义 - 你的浏览器支持基线
和你的团队、产品经理一起,根据你的用户数据,明确定义出你们产品需要支持的最低浏览器版本。比如,我们可以定义为:"所有主流浏览器最近两个大版本"。把它写进团队的开发规范文档里。这是一个契约,也是我们后续做决策的依据。
放弃全量引入,按需分析
请立刻从你的项目入口文件里,删掉 import 'core-js/stable'或import 'babel-polyfill' 这种一刀🔪切的写法。
我们应该完全信赖构建工具的静态分析能力。比如,在babel.config.js
里这样配置:
JavaScript
presets: [
['@babel/preset-env', {
useBuiltIns: 'usage', // 关键配置
corejs: 3
}]
]
useBuiltIns: 'usage'
这个选项,意味着Babel会自动检测你的代码,只把你代码中用到了的 、并且在你目标浏览器中不支持的 那些新特性,才引入对应的polyfill
。这能极大地减小polyfill
的体积。
考虑使用Polyfill后端服务
对于一些大型应用,可以考虑使用Polyfill服务(比如polyfill.io的自建替代品,因为官方服务曾出现过稳定性问题, 现在好像都无法访问了😒)。
它的原理是:服务器根据请求的User-Agent头,判断出用户的浏览器版本,然后只下发这个浏览器所需要的polyfill脚本。这是最高效、最精准的方案。
拥抱渐进增强,接受优雅降级
(这是一个说烂了的话题, 说白了就是 摆烂🤔)
对于一些非核心的、锦上添花的新API(比如View Transitions API),我们可以不提供polyfill。在支持的浏览器上,用户能体验到炫酷的页面切换动画;在不支持的浏览器上,它就是一个普通的页面跳转。
不是所有功能,都值得我们用增加全体用户性能负担的方式,去强行兼容。
作为开发者,我们的职责不只是实现功能,还包括控制我们产品的开发成本------这其中就包括了用户需要付出的加载成本。
为那1%的、甚至在你的用户数据里根本不存在的旧浏览器用户,让99%的现代浏览器用户去承担额外的加载负担,在2025年,这已经是一笔不划算的买卖了。
是时候检查一下你的项目了。打开你的打包分析报告,看看core-js
占了多大。
然后,问问你自己:"这些代码,真的有必要吗?"
欢迎大家讨论😎