Polyfill(polyfill)——在未原生实现的功能(API、方法、对象)的环境中模拟这些功能的代码。Polyfill允许在旧版浏览器和运行时环境中使用现代JavaScript、CSS或Web API功能。根据MDN Web Docs,polyfill是渐进增强和确保跨浏览器兼容性的关键工具。
要点
Polyfill——实现执行环境未原生支持功能的代码片段(通常是JavaScript)。该术语由Remy Sharp于2009年作为一个文字游戏提出:Polyfill——类比填缝剂(Polyfilla),填补墙壁上的裂缝。Polyfill填补了标准与特定浏览器或运行时环境支持之间的空白。
Polyfill不修改现有代码——它扩展执行环境。如果浏览器不支持Array.prototype.includes,polyfill会在主代码执行前将此方法添加到Array原型中。Polyfill可以模拟新的全局对象(Promise、Map、Set、Symbol)、静态方法(Array.from、Object.assign)和原型方法。
Feature detection——安装polyfill前的必要机制。不应检查user-agent(什么浏览器),而应检查方法是否存在:if (!Array.prototype.includes) { Array.prototype.includes = ... }。这保证了polyfill不会覆盖已存在的原生实现。Google Analytics和其他服务收集API支持数据用于分析。
首批polyfill出现在Internet Explorer 6–8时代(2005–2009),当时开发人员发现了W3C标准与浏览器实现之间的差距。该术语由Remy Sharp于2009年在BarCamp London会议上引入。第一个大规模polyfill是html5shiv(2009)——为Internet Explorer添加HTML5标签(<section>、<article>、<nav>)支持的库。
随着ES6(2015)的出现和ECMAScript的年度更新周期,所需polyfill的数量增加了。每年标准都会添加旧浏览器不支持的新方法(Array.includes、String.padStart、Object.fromEntries、Promise.allSettled)。2014年以es6-shim起步的core-js成为了通用解决方案。到2026年,core-js包含超过5000个针对ES5–ES2025的polyfill模块。
| 类别 | 可以polyfill | 不可以polyfill |
|---|---|---|
| 原型方法 | Array.includes、String.startsWith | — |
| 全局对象 | Promise、Map、Set、Symbol | — |
| 静态方法 | Object.assign、Array.from | — |
| 语言语法 | — | 箭头函数、async/await、class |
| Web API | fetch、IntersectionObserver | Service Worker(需要原生支持) |
转译将新语法转换为旧语法(const → var、() => {} → function() {})。Polyfill添加缺失的方法和对象(Promise、Array.includes)。这两种机制相辅相成:转译确保语法兼容性,polyfill确保API的完整性。Babel + core-js是全面支持的标准组合。
Babel @babel/preset-env配合useBuiltIns选项根据目标浏览器确定需要哪些polyfill。useBuiltIns: “usage”分析代码中使用了哪些API,只从core-js连接所需的polyfill。useBuiltIns: “entry”通过单个core-js/stable导入连接目标浏览器的所有polyfill。
// 检查polyfill是否存在并添加
if (typeof Array.prototype.includes !== "function") {
Object.defineProperty(Array.prototype, "includes", {
value: function(searchElement, fromIndex) {
if (this == null) {
throw new TypeError("Array.prototype.includes called on null or undefined");
}
var arr = Object(this);
var len = arr.length >>> 0;
if (len === 0) { return false; }
var start = fromIndex | 0;
var k = Math.max(start >= 0 ? start : len + start, 0);
while (k < len) {
if (arr[k] === searchElement) { return true; }
k++;
}
return false;
},
writable: true,
configurable: true,
});
}
// 使用——现在在任何浏览器中都是安全的
const arr = [1, 2, 3, 4, 5];
console.log(arr.includes(3)); // trueArray.prototype.includes的polyfill检查该方法是否在Array原型中定义。如果没有——通过Object.defineProperty创建属性,设置writable: true、configurable: true标志。实现遵循ES2016规范:检查null/undefined、转换为对象、处理负值的fromIndex。添加polyfill后,arr.includes(3)调用在所有浏览器中均有效,包括Internet Explorer 11。
core-js——最完整的JavaScript polyfill库,支持所有TC39 stage-4提案(ECMAScript标准)。core-js包含Promise、Symbol、Map、Set、WeakMap、WeakSet、Array方法、String方法、Object方法、Number方法、Math方法、Reflect、globalThis及所有stage-4 proposals的polyfill。当前版本core-js 3.38+覆盖ES5–ES2025。
core-js通过@babel/preset-env和useBuiltIns选项与Babel集成。没有这种集成,开发人员必须手动导入每个polyfill:import “core-js/stable/array/includes”。@babel/preset-env根据.browserslistrc中的目标浏览器自动添加必要的导入。这减少了打包体积——只连接所需的polyfill。
Fetch API——最常被polyfill的Web API之一。fetch的原生实现在Chrome 42+(2015)、Safari 10.1+(2017)、Firefox 39+(2015)中可用,但在Internet Explorer和旧版WebView中缺失。whatwg-fetch polyfill通过XMLHttpRequest模拟fetch。替代方案——使用isomorphic-fetch(针对Node.js和浏览器的polyfill)或不需要polyfill的通用axios库。
// 仅针对旧浏览器加载fetch polyfill
if (typeof self.fetch !== "function") {
import("whatwg-fetch").then(module => {
self.fetch = module.fetch;
console.log("fetch polyfill loaded");
});
}
// 使用fetch(与polyfill和原生API均兼容)
async function loadData() {
try {
const response = await fetch("https://api.example.com/data");
const json = await response.json();
return json;
} catch (error) {
console.error("Failed to load:", error);
}
}通过import()动态导入fetch polyfill确保现代浏览器不加载多余代码。Polyfill异步加载,不阻塞主线程。加载后,self.fetch替换原生实现或添加缺失的实现。这是一种渐进增强技术:现代浏览器只获得原生代码,旧浏览器获得额外的polyfill。
// babel.config.js — core-js + preset-env
module.exports = {
presets: [
["@babel/preset-env", {
useBuiltIns: "usage",
corejs: {
version: "3.38",
proposals: true,
},
targets: {
browsers: ["> 0.5%", "not dead", "not op_mini all"],
},
}],
],
};# .browserslistrc — 目标浏览器
> 0.5%
last 2 versions
not dead
not op_mini all
ie >= 11
not ios_saf < 12useBuiltIns: “usage”分析代码,只添加实际使用的polyfill。corejs.version指示项目中core-js的版本。targets.browsers确定浏览器的最低级别——浏览器越老,连接的polyfill越多。.browserslistrc不仅被Babel使用,还被Autoprefixer、PostCSS和Stylelint用于一致的目标定位。
Polyfill.io——一种服务(和同名的库),动态确定用户浏览器需要哪些polyfill并只返回这些polyfill。Polyfill.io使用User-Agent头部确定浏览器版本,并提供最少的polyfill集。与通用的polyfill打包相比,这减少了传输的数据量。
通过应用程序主代码前的<script>标签连接Polyfill.io。服务分析User-Agent并返回仅包含该浏览器所需polyfill的JavaScript文件。Chrome不会获得任何polyfill,IE 11将获得完整集合。这是性能最佳的方法:现代浏览器不加载多余代码。
<!-- Polyfill.io:动态加载 -->
<script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=Promise%2CArray.prototype.includes%2CObject.assign%2Cfetch"></script>
<!-- Polyfill.io的本地版本 -->
<script src="/js/polyfill.js"></script>
<script>
// 针对fetch的特性检测
if (!self.fetch) {
loadScript("/js/fetch-polyfill.js");
}
</script>URL中的features参数指定要加载哪些polyfill。可能的值包括:方法名(Array.prototype.includes)、全局对象(Promise)或标志(es6、es2016)。“default”标志为现代JavaScript连接基础集。对于生产项目,建议在自己的CDN上托管Polyfill.io或使用库的本地版本以控制可用性。
WebView在移动应用中(Android WebView、iOS上的WKWebView)——一个特殊的polyfill环境。WebView版本取决于操作系统版本和已安装的Chrome System WebView(Android)或iOS Safari的WKWebView更新。在旧版Android(4.4、5.0)中,WebView基于Chromium 30–37——不支持fetch、Promise、IntersectionObserver。
React Native使用JavaScriptCore(iOS)或Hermes(Android)——这些引擎以不同方式实现ES6+。iOS上的JavaScriptCore支持大多数ES6功能,但可能缺少某些stage-3提案。Hermes(React Native 0.70+中默认使用)支持有限的ES标准集——对于它,polyfill是必需的。
// 针对WebView的特性检测
const polyfills = [];
// Promise
if (typeof Promise === "undefined") {
polyfills.push("Promise");
}
// Fetch API
if (typeof self.fetch === "undefined") {
polyfills.push("fetch");
}
// IntersectionObserver(用于懒加载)
if (typeof IntersectionObserver === "undefined") {
polyfills.push("IntersectionObserver");
}
// Polyfill的动态加载
if (polyfills.length > 0) {
const script = document.createElement("script");
script.src = "https://cdn.polyfill.io/v3/polyfill.min.js"
+ "?features=" + polyfills.join(",");
document.head.appendChild(script);
}针对WebView的Feature detection检查关键API(Promise、fetch、IntersectionObserver)是否存在,并只为缺失的API动态加载polyfill。这确保现代WebView(Android 12上的Chrome 100+)不加载多余代码,而旧版WebView(Android 5.0)获得必要的支持。
常见问题
React Native在Hermes上需要对某些ES方法进行polyfill:Array.flat、Array.flatMap、globalThis、TextEncoder。建议为生产构建连接core-js或react-native-polyfill-globals。iOS上的JavaScriptCore支持更多功能,但也可能需要对stage-3提案进行polyfill。
Polyfill会将性能降低1–5%,因为JavaScript实现比引擎中原生的C++实现更慢。例如,纯JS中的polyfill Promise比V8中原生Promise慢。但对于大多数应用而言,差异并不明显。对于关键代码,建议通过feature detection检查原生实现是否存在。
转译转换语法:const → var、箭头函数 → function。Polyfill添加新对象/方法:Promise、Array.includes、fetch。转译在构建阶段工作,polyfill在运行时加载。这两种机制对于在旧环境中全面支持现代代码都是必需的。
可以,如果目标受众只使用现代浏览器(Chrome 90+、Safari 15+、Firefox 90+)。对于支持旧设备或企业用户(Internet Explorer 11仍在政府部门使用)的项目,polyfill是必需的。通过Google Analytics分析受众的浏览器统计信息。
core-js完整构建约~85 KB(gzip)。在Babel中使用useBuiltIns: “usage”时,只连接所需的polyfill,根据目标浏览器将体积减小到5–30 KB。对于现代浏览器(Chrome 100+),可能不需要任何polyfill。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。