什么是 Polyfill?如何按需加载 Polyfill?
Polyfill
Polyfill(填充物):为旧浏览器提供新 API 的实现。
// 新 API:Array.prototype.includes(ES2016)
[1, 2, 3].includes(2) // 旧浏览器不支持
// Polyfill
if (!Array.prototype.includes) {
Array.prototype.includes = function(searchElement) {
return this.indexOf(searchElement) !== -1
}
}Polyfill vs Transpile
| 方式 | 作用 | 示例 |
|---|---|---|
| Transpile | 语法转译 | 箭头函数 → function |
| Polyfill | API 补丁 | Promise、Array.includes |
// Transpile(Babel 处理语法)
const fn = () => {} → var fn = function() {}
// Polyfill(需要运行时补丁)
new Promise(resolve => resolve()) // 旧浏览器没有 Promisecore-js
最常用的 polyfill 库:
// 全量引入(不推荐)
import 'core-js' // 引入所有 polyfill,体积大
// 按需引入
import 'core-js/stable/promise'
import 'core-js/stable/array/includes'@babel/preset-env + useBuiltIns
// .babelrc
{
"presets": [
["@babel/preset-env", {
"useBuiltIns": "usage", // 按代码使用情况自动引入
"corejs": 3
}]
]
}useBuiltIns 选项:
false:不自动引入 polyfillentry:在入口文件全量引入(根据 browserslist 裁剪)usage:按代码实际使用情况按需引入(推荐)
// 源代码
const arr = [1, 2, 3]
arr.includes(2)
Promise.resolve(1)
// useBuiltIns: "usage" 自动注入
import "core-js/modules/es.array.includes.js"
import "core-js/modules/es.promise.js"browserslist
# .browserslistrc
> 0.5%
last 2 versions
not dead
not ie 11Babel、Autoprefixer、ESLint 等工具共用此配置,决定需要兼容哪些浏览器。
动态 Polyfill(CDN 服务)
不用打包 polyfill,而是根据浏览器 UA 动态返回:
<!-- polyfill.io -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=es2015,es2016"></script>服务器根据 UA 返回该浏览器需要的 polyfill,减少不必要的下载。
⚠️ 2024 年 polyfill.io 被收购后出现过安全问题,建议使用替代方案:
cdn.polyfill.io→ Cloudflare 镜像- 自建 polyfill 服务
模块/Nomodulule 模式
<!-- 现代浏览器加载 ESM -->
<script type="module" src="modern.js"></script>
<!-- 旧浏览器加载降级版本 -->
<script nomodule src="legacy.js"></script>构建时生成多版本
// Vite 配置(现代 + 降级)
import legacy from '@vitejs/plugin-legacy'
export default {
plugins: [
legacy({
targets: ['ie >= 11'],
modernPolyfills: true
})
]
}
// 生成:
// modern.js(现代浏览器,更小)
// legacy.js(旧浏览器,含 polyfill)Polyfill 加载策略对比
| 方案 | 体积 | 准确性 | 维护 |
|---|---|---|---|
| 全量引入 | 大 | 100% | 简单 |
| useBuiltIns: usage | 小 | 高 | 自动 |
| 动态 CDN | 最小 | 高 | 依赖服务 |
| module/nomodule | 最优 | 高 | 需构建配置 |
最佳实践
- 使用
useBuiltIns: "usage"自动按需引入 - 配置合理的 browserslist
- 考虑 module/nomodule 双版本
- 尽量不兼容 IE(2022年已退役)