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)。 - 缺点:
- 污染全局环境:如果你写的是一个库 (Library),这会给使用你库的项目带来麻烦。
- 体积巨大:即使只用了一个 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).

