BabelJs - Babel Polyfill
BabelJS - 现代 API 的垫片(Polyfills)
Section titled “BabelJS - 现代 API 的垫片(Polyfills)”虽然 Babel 擅长转译现代 JavaScript 语法,但它本身并不提供新 API 的实现(例如 Promise、Map、Set,或者内置对象的新方法,如 Array.prototype.includes)。这些功能由垫片(polyfills)处理。本章将解释如何使用 Babel 集成垫片,以确保您的现代 JavaScript 代码在旧环境中也能正确运行。
以前,@babel/polyfill 是一个常用的包,但它已被弃用。现代的方法是使用 core-js(一个全面的垫片库)和 regenerator-runtime(用于 generators 和 async 函数),通常由 @babel/preset-env 自动管理。
通常需要垫片(Polyfills)的常见特性
Section titled “通常需要垫片(Polyfills)的常见特性”以下是通常需要垫片以支持旧版浏览器的常见 JavaScript 特性列表:
- Promise (
Promise) - 集合 (
Map、Set、WeakMap、WeakSet) - Symbol (
Symbol) - 迭代器和生成器(Iterators and Generators)(需要
regenerator-runtime) - Async/Await(需要
regenerator-runtime和Promise垫片) - 内置对象的静态方法(例如,
Array.from、Object.assign、Object.entries) - 内置对象的实例方法(例如,
Array.prototype.includes、String.prototype.padStart)
使用 @babel/preset-env 配置垫片(Polyfills)
Section titled “使用 @babel/preset-env 配置垫片(Polyfills)”处理垫片的推荐方法是配置 @babel/preset-env。这个预设可以根据您的代码和目标环境自动包含来自 core-js 的垫片。
首先,确保您已安装必要的包。创建一个新项目:
mkdir babel-polyfill-examplecd babel-polyfill-examplenpm init -y安装 Babel 和 core-js:
npm install --save-dev @babel/core @babel/cli @babel/preset-envnpm install --save core-js regenerator-runtime您的 package.json 中的 devDependencies 将类似于以下内容(版本可能有所不同):
{ "name": "babel-polyfill-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "babel src -d lib" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/cli": "^7.x.x", "@babel/core": "^7.x.x", "@babel/preset-env": "^7.x.x" }, "dependencies": { "core-js": "^3.x.x", "regenerator-runtime": "^0.x.x" }}现在,在您的项目根目录中创建一个 Babel 配置文件 babel.config.json(或 .babelrc.json):
{ "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", "corejs": 3, "targets": { "browsers": ["> 0.25%", "not dead", "ie >= 11"] } } ] ]}配置说明:
"useBuiltIns": "usage":这告诉 Babel 分析您的代码,并自动为那些被使用且您的目标环境不支持的垫片添加core-js模块的导入。这非常高效,因为它只包含所需的部分。"corejs": 3:指定您正在使用core-js版本 3。"targets":定义您要支持的目标环境。垫片将根据这些环境中缺失的特性添加。您可以指定浏览器查询、Node 版本等(更多信息请参见browserslist文档:https://github.com/browserslist/browserslist)。
或者,如果使用 "useBuiltIns": "entry",您需要在主入口文件(例如 src/index.js)的开头手动添加 import "core-js/stable"; 和 import "regenerator-runtime/runtime";。然后 Babel 将根据您的 targets 用特定的导入替换这些语句。
我们创建一个 src 目录并添加一些示例。
示例 1: Promise
Section titled “示例 1: Promise”创建 src/promises-example.js:
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved after 1 second!"); }, 1000);});
myPromise.then(message => { console.log(message);});
async function fetchData() { await myPromise; console.log("Data fetched after promise resolved (using async/await).");}
fetchData();使用 package.json 中的构建脚本编译此文件:
npm run build这将在 lib/promises-example.js 中创建转译后的文件。如果您检查转译后的代码,您会看到 Babel 已经转换了 async/await 语法,并且由于 useBuiltIns: "usage" 和您的 targets 设置,如果 Promise 或 async 函数未完全被您的目标环境(例如 IE 11)支持,它会注入 import 语句,例如 import 'core-js/modules/es.promise.js' 和 import 'regenerator-runtime/runtime.js'。
要在浏览器中运行此代码,通常会使用像 Webpack 或 Parcel 这样的打包工具来解析这些导入并创建一个单独的打包文件。为了进行简单的测试,您可以创建一个 index.html 文件:
<html><head> <title>Babel Polyfill Test</title></head><body> <h1>Testing Promises with Polyfills</h1> <!-- In a real app, you'd use a bundler. This direct script tag won't work if Babel added import statements for polyfills. For this example, assume you bundle lib/promises-example.js into a single bundle.js --> <!-- <script src="path/to/your/bundled_promises_example.js"></script> --> <p>Open your browser's console to see the output.</p> <p>If bundling 'lib/promises-example.js' and running it, the console would show:</p> <pre>Promise resolved after 1 second!Data fetched after promise resolved (using async/await). </pre></body></html>注意:直接包含 babel-polyfill/dist/polyfill.min.js 的旧方法已不再是标准做法。现代工具链依赖于由 @babel/preset-env 和打包工具管理的模块化垫片。
示例 2: 字符串填充
Section titled “示例 2: 字符串填充”String.prototype.padStart() 和 String.prototype.padEnd() 是在 ES2017 中引入的。
创建 src/string-padding-example.js:
const str = 'Babel';
console.log(str.padStart(10, '_')); // Expected: '_____Babel'console.log(str.padEnd(10, '*')); // Expected: 'Babel*****'运行 npm run build 后,将生成 lib/string-padding-example.js。如果您的 targets 包括不支持字符串填充的浏览器,Babel(通过 core-js)将确保包含必要的垫片(如果可能,也会进行转换)。当在这样的浏览器中打包并运行时,控制台输出将是:
_____BabelBabel*****示例 3: Map 和 Set
Section titled “示例 3: Map 和 Set”Map 和 Set 是 ES2015 的特性。
创建 src/collections-example.js:
let myMap = new Map();myMap.set("name", "BabelJS");myMap.set("type", "Transpiler");console.log(myMap.get("name"));
let mySet = new Set();mySet.add(1);mySet.add(5);mySet.add(5); // duplicate, will be ignoredmySet.add(10);console.log(mySet.has(5));console.log("Set size:", mySet.size);在运行 npm run build 后,如果目标环境较旧,lib/collections-example.js 中的转译代码在打包并运行时,将输出:
BabelJStrueSet size: 3示例 4: 数组方法
Section titled “示例 4: 数组方法”Array.prototype.includes (ES2016) 和 Array.from (ES2015)。
创建 src/array-methods-example.js:
const numbers = [1, 2, 3, 4, 5];console.log("Includes 3:", numbers.includes(3)); // Expected: trueconsole.log("Includes 6:", numbers.includes(6)); // Expected: false
const newArray = Array.from("hello");console.log("Array from string:", newArray); // Expected: ['h', 'e', 'l', 'l', 'o']在运行 npm run build 后,lib/array-methods-example.js 中的转译代码在打包并运行时,将输出:
Includes 3: trueIncludes 6: falseArray from string: ["h", "e", "l", "l", "o"]重要注意事项
Section titled “重要注意事项”- 打包工具至关重要:对于浏览器应用,使用模块打包工具(如 Webpack、Parcel 或 Rollup)是必不可少的。打包工具会解析 Babel 可能添加的
core-js模块和regenerator-runtime的import语句,为您的index.html创建一个(或几个)JavaScript 文件。 - 目标环境设置:在 Babel 配置中仔细定义您的
targets。这可以确保垫片仅包含在实际需要它们的浏览器/环境中,从而优化您的打包体积。 core-js版本:始终在 Babel 配置中指定corejs版本,以确保行为一致并避免警告。core-js@3是现代标准。- 全局污染:
core-js垫片可能会修改全局对象(例如,Promise、Array.prototype)。这对于应用来说通常没有问题,但对于库来说可能需要考虑。库可能更倾向于使用@babel/plugin-transform-runtime来避免全局污染,它提供了不会修改全局对象的辅助函数。
通过利用 @babel/preset-env 与 core-js 协同工作,您可以自信地使用现代 JavaScript API,并确保您的应用在各种环境中具有良好的健壮性和兼容性。有关 core-js 及其功能的详细信息,请访问:https://github.com/zloirock/core-js