Babel Polyfill (垫片) 原理与演进

1. 面试题

Q: @babel/polyfill 为什么被弃用了?如何配置才是现代的最佳实践 (useBuiltIns 与 runtime-corejs3)?

2. 核心解答

Babel 可以转换新 语法 (Syntax) (如 let, =>),但不能转换新 API (Built-in) (如 Promise, Set, Array.from)。这就需要 Polyfill (垫片)

(1) 旧时代的 @babel/polyfill

  • 原理:直接修改全局对象 (window.Promise, Array.prototype.includes)。
  • 缺点
    1. 污染全局环境:如果你写的是一个库 (Library),这会给使用你库的项目带来麻烦。
    2. 体积巨大:即使只用了一个 Promise,也会把整个 core-js 全量引入,导致包体积暴增。
  • 现状:已在 Babel 7.4.0 废弃。

(2) 现代方案 1:@babel/preset-env + useBuiltIns: 'usage'

这是目前应用开发 (Application Development) 的主流方案。

  • 原理:Babel 分析你的代码,发现你用了 Promise,就自动插入 import "core-js/modules/es.promise"
  • 优点:按需引入,体积最小。
  • 缺点:依然会污染全局环境 (修改原型)。

(3) 现代方案 2:@babel/plugin-transform-runtime + corejs: 3

这是开发 工具库 (Library/SDK) 的最佳实践。

  • 原理:将 API 转换成对 core-js-pure 中函数的调用,不污染全局对象
    • 源代码:Promise.resolve()
    • 转换后:import _Promise from "@babel/runtime-corejs3/core-js-stable/promise"; _Promise.resolve()
  • 优点:沙箱化 (Sandboxed),不污染全局。复用 Helper (辅助函数),减小体积。
  • 缺点:不能 polyfill 实例方法 (如 [].includes('a') ),因为这需要修改 Array.prototype。但在 core-js@3 中已经支持了实例方法的 polyfill (通过 call 指向新的函数)。

3. 面试加分项

Q: 什么时候必须用 Polyfill?

如果你的目标浏览器 (Target) 不支持某些 ES6+ 特性 (如 IE11 不支持 Promise)。现代浏览器大多原生支持,这时候 Polyfill 代码就是累赘。Babel 的 preset-env 会根据 browserslist 智能判断是否需要 Polyfill,这是目前最完美的平衡方案。

4. 总结

  • Old: Global Polyfill (Deprecated).
  • App: preset-env + usage (Global pollution ok).
  • Lib: transform-runtime (No pollution).