什么是 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())  // 旧浏览器没有 Promise

core-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 选项:

// 源代码
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 11

Babel、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 最优 高 需构建配置

最佳实践

  1. 使用 useBuiltIns: "usage" 自动按需引入
  2. 配置合理的 browserslist
  3. 考虑 module/nomodule 双版本
  4. 尽量不兼容 IE(2022年已退役)

同分类其他题目