2026年还有人在使用Polyfill吗?
2026年还需要 polyfill 吗?检查 core-js、Browserslist 和 Babel,移除冗余负载,保留 Temporal,并弃用 polyfill.io。
是的——但几乎没有人还在用你的打包工具所继承的那些。2019年合理的全量polyfill默认配置——针对宽泛的Browserslist目标,使用useBuiltIns: 'entry'运行@babel/preset-env——如今只是在向绝大多数用户发送无用的冗余代码,而这些代码所支持的浏览器份额已经趋近于零。polyfill在2026年仍然是处理一小批明确列出的、真正不受支持的特性的正确工具——但对于其他一切而言,它已经成了一种负担。
本文旨在解决一个核心问题:你当前构建流水线中的polyfill是否仍然物有所值。文章将指出哪些API是无效负担,哪些仍然合理地需要polyfill,polyfill.io供应链事件如何改变了加载方式,以及用于审计实际发布内容的具体命令。
核心要点
- 对于2026年面向常青浏览器的React、Vue、Svelte或Next.js应用,大多数团队所需的polyfill数量接近于零——明确的例外包括Decorators、Temporal跨浏览器过渡期间,以及受限的企业级浏览器约束。
Array.flat、Object.entries、Promise.allSettled、structuredClone、可选链、空值合并运算符和fetch在2026年均已获得广泛支持;对它们进行polyfill只是在发送几乎没有真实用户需要的冗余字节。- 远程polyfill服务模式(从polyfill.io加载脚本)在2024年供应链事件后已经终结;Cloudflare和Fastly虽然搭建了镜像,但这种模式本身已不再站得住脚。
- 运行
npx browserslist查看你的配置实际针对哪些浏览器,然后使用source-map-explorer找出你正在发布的core-js体积。 - web.dev的Enhancement / Additive / Critical分类框架是判断某个缺失特性是否值得polyfill的最有力工具。
2026年的原生支持基线:哪些特性不再需要polyfill
大多数构建配置仍在例行polyfill的JavaScript特性,如今已在所有常青浏览器中获得支持——这意味着对它们进行polyfill只是在发布几乎没有真实用户需要的冗余字节。那些曾经是polyfill教程核心的特性——Math.trunc、Array.prototype.flat、可选链——如今已获得广泛支持,正是2026年审计应当清除的无效负担。
最具代表性的教学示例——javascript.info的Math.trunc polyfill——是最清晰的说明。Math.trunc自2015年起就已获得所有浏览器的普遍支持——根据MDN的兼容性数据,它在Chrome 38、Firefox 25和Safari 8中就已发布。在2026年编写或打包Math.trunc polyfill,无异于为一个没有人在运行的浏览器添加保护代码。
对于那些过时配置仍在针对的整个API层面,情况同样如此。下表的支持率数据来源于caniuse.com全球使用数据(StatCounter,2026年5月);“全球支持率”指caniuse在所有追踪版本中加权计算的浏览器份额。
| 特性 | 全球支持率(caniuse,2026年) | 是否仍值得polyfill? |
|---|---|---|
Array.prototype.flat | 94.11% | 否 |
Object.entries | 95.06% | 否 |
Promise.allSettled | 94.04% | 否 |
structuredClone | 93.84% | 否 |
可选链(?.) | 93.99% | 否(语法特性——应转译,而非polyfill) |
空值合并运算符(??) | 93.99% | 否(语法特性——应转译,而非polyfill) |
fetch | 96.3% | 否 |
Temporal | 65.16% | 是——过渡期内 |
| Decorators | 原生支持率0% | 是 |
Array.flat、Object.entries、Promise.allSettled、structuredClone、可选链、空值合并运算符和fetch的全球支持率均在94%至96%左右——如果你的Babel配置仍在对这些特性进行polyfill,你正在发布几乎没有任何用户需要的冗余字节。
语法特性与函数的区别: 可选链和空值合并运算符是语法,而非缺失的函数,因此它们由转译器处理,而非polyfill。正如javascript.info所述:对现代语法或运算符使用转译器,对缺失的函数使用polyfill。这一区别在审计时至关重要——配置中出现
??的”polyfill”,说明配置将两者混淆了。
2026年哪些polyfill仍然物有所值?
Discover how at OpenReplay.com.
polyfill对于一小批明确的场景仍然是正确的工具:在任何地方都没有原生支持的特性、正在跨浏览器过渡的特性,以及常青浏览器假设不成立的锁定浏览器环境。这些例外情况,才是在你的流水线中保留polyfill机制的合理理由。
Decorators。 TC39 decorators提案处于Stage 3阶段,尚无任何浏览器原生实现。如果你使用了decorators——无论是直接使用,还是通过Angular等框架或依赖它们的库间接使用——你就依赖于转译器以及某些情况下的运行时辅助代码。目前还没有”等待原生支持”的选项;该特性尚未在浏览器中发布。
Temporal,在其跨浏览器过渡期间。 Temporal是一个已完成的TC39提案——它出现在TC39已完成提案列表中——并已开始原生发布。根据MDN的Temporal兼容性数据,它已在Firefox 139+、Chrome 144+、Edge 144+和Node.js 26+中可用,而Safari仍缺乏已启用的支持。这种不均衡的推出使Temporal polyfill成为一种过渡性需求,而非永久性需求。TC39的proposal-temporal仓库将@js-temporal/polyfill列为alpha版本选项之一,同时还有其他替代方案——它并非唯一的官方选择。
企业级和锁定浏览器环境。 受监管的金融系统、政府内网以及自助服务终端或POS设备,有时会将浏览器版本固定多年。如果你的真实用户群包含无法更新的浏览器,你的Browserslist目标就比常青浏览器默认值更宽泛,某些polyfill就是不可或缺的。关键词是真实——这种情况被断言的频率远高于被实际测量的频率。在以此为由保留polyfill之前,请先通过分析数据加以验证。
通过JS polyfill支持的有限可用CSS特性。 某些CSS功能,例如旧版Safari上的容器查询,在推出期间曾通过JavaScript进行polyfill。正如web.dev所指出的,容器查询polyfill使用ResizeObserver和MutationObserver来模拟原生行为——其README已将其标记为不再维护。这些polyfill带有下文将介绍的行为注意事项。
更广泛评论中的软共识——“使用有针对性的polyfill,基于数据做决策”——并没有错,但它低估了已经发生的变化程度:对于2026年的大多数应用而言,“polyfill继续发挥重要作用”已不再成立。这一作用现在是狭窄且明确的。
polyfill.io事件终结了远程polyfill服务模式
远程polyfill服务模式——从第三方CDN加载<script>,返回针对浏览器定制的polyfill——在2024年polyfill.io供应链攻击事件后,已不再是可辩护的架构。这一事件迫使团队审计他们从第三方加载的内容,大多数团队发现他们根本不需要其中的大部分内容。
事件时间线:
- 2024年初——所有权变更。 polyfill.io域名易主。Fastly的社区帖子记录了这一变更及Fastly对受影响用户的应对措施。
- 2024年6月25日——恶意软件报告。 安全公司Sansec报告称,polyfill.io服务已向其向终端用户提供的脚本中注入恶意软件。
- 2024年6月——镜像和缓解响应。 Cloudflare宣布自动将polyfill.io链接替换为其自有镜像,Fastly也提供了替代端点。
镜像使现有站点得以继续运行,但并不能为这种模式正名。此次事件暴露了一种标准的生产故障模式:团队在cdn.polyfill.io脚本标签中加载了多年前就已不再需要的polyfill包,在每次页面加载时执行第三方JavaScript,以支持那些早已更新的浏览器。不要将Cloudflare或Fastly镜像视为”安全”的直接替代品——应将这次事件视为彻底移除脚本标签的契机,将真正需要的polyfill移入你自己的bundle中,由你自己控制和审查。
如何审计你的打包工具实际发布的内容
审计polyfill占用是一个四步流程:读取你的真实Browserslist目标、检查bundle中的core-js体积、修正Babel配置,并对照真实用户数据进行验证。这一切都不需要猜测——每个步骤都是你今天就可以运行的命令或配置差异对比。
第一步:查看你的真实目标列表
Browserslist查询决定了你的配置支持哪些浏览器,而了解你的配置实际解析结果的唯一可靠方式,就是直接询问它:
npx browserslist
这会打印出你当前配置所针对的具体浏览器版本列表。Browserslist默认值(> 0.5%, last 2 versions, Firefox ESR, not dead)已通过not dead排除了IE11。真正的风险在于继承的自定义查询——一个随意的> 0.2%或多年前的明确版本下限——会悄然引入旧目标。运行这个命令;如果输出中包含没有真实用户在使用的浏览器,那就是你的无效负担。
第二步:找出bundle中的core-js体积
core-js是@babel/preset-env注入polyfill所使用的库,它可能占据不需要它的bundle的相当大比例。检查生产构建:
npx source-map-explorer dist/assets/*.js
source-map-explorer通过source map将每个bundle的内容渲染为树状图。查找core-js模块——es.array.flat、es.object.entries、es.promise.all-settled。上述每一个都对应无效负担表中的某个特性。(对于webpack项目,webpack-bundle-analyzer提供相同的视图。)
第三步:修正Babel配置
大多数团队继承的标准配置是不完整的。常见的模式如下:
{
"presets": [
["@babel/preset-env", {
"useBuiltIns": "usage",
"corejs": 3
}]
]
}
上述配置存在两个问题。首先,没有targets字段,因此配置会回退到你的Browserslist文件——这可能正是第一步中那个过时的自定义查询。其次,corejs: 3未固定版本;根据core-js文档,版本应指定到次版本号,以便preset-env注入与已安装版本匹配的polyfill。适合2026年的版本配置如下:
{
"presets": [
["@babel/preset-env", {
"targets": "> 1%, last 2 versions, not dead",
"useBuiltIns": "usage",
"corejs": "3.40"
}]
]
}
将corejs固定到你实际安装的次版本号,而不是照抄任何数字——preset-env使用它来决定注入哪些polyfill。
useBuiltIns的值是最关键的设置。根据@babel/preset-env文档:
'entry'将单个import 'core-js'替换为你的targets所需的完整polyfill集合——范围宽泛,是大多数继承冗余的根源。'usage'仅为你的代码实际引用的特性添加polyfill,按文件处理。这几乎总是你想要的:它将polyfill注入范围限定在真实使用情况,而非整个目标矩阵。
第四步:对照真实用户数据进行验证
配置是关于用户的假设;RUM数据对其进行确认或反驳。web.dev建议在决定是否polyfill之前先测量真实的特性支持情况,并将RUM Insights作为广泛的数据来源。其中的规律是:Baseline Widely available集合中的特性被98%或更多用户所支持。如果你的分析数据显示某个特性接近这一阈值,那么针对它的polyfill所服务的用户比例已经微乎其微。
何时应该使用polyfill?Enhancement、Additive、Critical框架
当某个特性在你的真实用户群中确实没有获得广泛支持时,web.dev的Enhancement / Additive / Critical分类是决定是否polyfill的最有用启发式框架:如果某个缺失特性对用户不可见,就不要polyfill;如果它能优雅降级,倾向于不polyfill;只有当缺失会破坏用户体验时,polyfill才值得其性能代价。
web.dev定义的三个层级如下:
- Enhancement(增强型) ——该特性能改善体验,但其缺失不会产生任何视觉变化或功能损失。
fetchpriority等性能提示就是例子。不支持该特性的浏览器上的用户不会察觉。不要polyfill。 - Additive(附加型) ——该特性可能影响页面的外观或行为,但不会造成严重问题;用户可能只有在跨浏览器对比时才会注意到。如果存在polyfill,倾向于不使用它,尤其是在你已经polyfill了其他内容的情况下。颜色函数和subgrid就是例子。
- Critical(关键型) ——缺失会导致体验损坏:运行时错误、布局破坏、功能不可用。在这种情况下,polyfill(或完全不同的方案)是合理的。
web.dev的锚定规则与上面的支持率表完美配合:如果某个特性已经Widely available,你就不应该使用polyfill——除非你有关于用户的数据明确告诉你情况不同。
为什么polyfill的bug难以发现——以及会话回放如何将其暴露
polyfill的bug属于标准测试套件完全无法捕获的一类:它们只在触发polyfill的那部分浏览器中出现,而这些浏览器从来不是你的CI所运行的常青浏览器。会话回放是少数能够捕获这些用户真实DOM状态的可观测性技术之一,因为它记录了运行polyfill路径的浏览器中真实的交互和变更序列。
会话回放能够暴露polyfill代码路径与原生代码路径之间的三种具体差异模式:
- 容器查询polyfill中的布局偏移时序。 正如web.dev所指出的,容器查询polyfill通过
ResizeObserver回调驱动布局,这些回调在浏览器绘制新帧之前触发——增加了呈现延迟并影响Interaction to Next Paint。延迟的重绘时序可能产生布局偏移,而这只能在需要polyfill的浏览器中观察到。 - Temporal时区差异。 在Temporal的跨浏览器过渡期间,团队可能需要同时测试原生和polyfill版本的
Temporal实现,以确保跨浏览器行为一致。你的测试套件可能只针对一种实现路径运行,而真实用户却遇到另一种;来自原生实现和polyfill实现共存的浏览器的回放,有助于暴露这些差异。 - 焦点管理缺陷。 以无障碍访问为目的的polyfill,可能在缺乏原生支持的浏览器中引入微妙的键盘导航和焦点管理问题——这正是web.dev更广泛强调的故障模式。带有键盘事件捕获的回放,能够显示用户在模态框背景中使用Tab键导航的情况,而针对polyfill的自动化测试通过后并不会揭示这一问题。
每种情况的共同因素都是一样的:bug存在于你的开发环境永远无法重现的用户群中。这正是减少polyfill发布的结构性理由——你移除的每一条polyfill路径,都是一条你不再需要监控的差异路径。
本周可以采取的具体行动
减少polyfill占用是一系列小型、可逆的编辑,每一步都需要对照bundle和用户数据进行验证。目标是在可能的地方使用原生实现,对真正的长尾情况进行条件加载,并丢弃其他一切。
- 运行
npx browserslist,删除任何针对没有真实用户在使用的浏览器的继承自定义查询。回退到更严格的当前目标,是单一杠杆效应最高的变更。 - **在
@babel/preset-env配置中设置useBuiltIns: 'usage'**和明确的targets字段,并将corejs固定到你已安装的次版本号。 - 使用
source-map-explorer对比修改前后的bundle差异,确认core-js体积确实下降,然后运行测试以验证没有丢失真正需要的内容。 - 彻底移除任何
cdn.polyfill.io脚本标签;将真正需要的polyfill移入你自己的bundle。 - 对真正的例外情况进行条件加载。 对于Temporal的过渡期,仅为需要它的浏览器加载polyfill,而不是向所有人发布。对于Decorators,在需要的地方依赖转译和运行时辅助代码。web.dev关于有限可用特性的polyfill应进行条件加载的观点,在这里直接适用。
- 在修改前后使用RUM数据进行验证,使审计建立在你的用户数据上,而非全球平均值上。
结论
对于2026年是否还有人使用polyfill这一问题,诚实的答案是:有——但可辩护的列表既短且具体:Decorators、Temporal在跨越浏览器鸿沟期间,以及你实际测量过的锁定浏览器环境。你的配置所继承的其他一切,都是花费在浏览器份额趋近于零上的字节,而polyfill.io事件提醒我们,粗心地加载它们会带来真实的风险。今天就从npx browserslist开始;它打印出的内容与你的用户实际运行的浏览器之间的差距,就是你的polyfill预算——对大多数团队而言,这个预算远比配置所假设的要小得多。
常见问题
polyfill和转译器有什么区别?
转译器在构建时将现代语法重写为旧版等价形式,而polyfill在运行时添加缺失的函数或API实现。可选链和空值合并运算符等语法特性由转译器处理,因为它们是语言运算符,而非可调用的函数。Promise.allSettled或structuredClone等API由polyfill处理,因为它们是运行时代码可以提供的缺失方法。配置中出现空值合并运算符的'polyfill',说明两者已被混淆。
babel-preset-env中useBuiltIns的'usage'和'entry'有什么区别?
将useBuiltIns设置为'entry'时,Babel会将单个core-js导入替换为你的targets所需的完整polyfill集合,这是大多数继承bundle冗余的根源。设置为'usage'时,Babel仅为你的代码实际引用的特性注入polyfill,按文件处理。'usage'几乎总是更优选的,因为它只为真实使用情况发布polyfill,而非针对整个目标矩阵。两者都需要将corejs版本固定到你已安装的次版本号。
通过Cloudflare或Fastly镜像继续使用polyfill.io是否安全?
Cloudflare和Fastly镜像使现有站点得以继续运行,但并不能为远程polyfill服务模式正名。2024年初polyfill.io域名易主,2024年6月25日Sansec报告该服务正在注入恶意软件之后,从第三方CDN加载浏览器定制polyfill对于注重安全的团队而言已无法辩护。彻底移除cdn.polyfill.io脚本标签,将真正需要的polyfill移入你自己的bundle,由你自己控制和审查。
如果我的应用只针对常青浏览器,还需要core-js吗?
对于2026年大多数面向常青浏览器的应用,core-js几乎不会发布任何有用的polyfill,因为Array.flat、Object.entries、Promise.allSettled、structuredClone和fetch等特性已经获得广泛支持。明确的例外是没有任何浏览器原生实现的Decorators,以及在不均衡跨浏览器推出期间的Temporal。对生产构建运行source-map-explorer,查看存在哪些core-js模块,然后收紧你的Browserslist目标并将useBuiltIns切换为'usage',以清除无效负担。