corejs放弃开源!细数前端10百万级热门Polyfill!
大家好,很高兴又见面了,我是" 高级前端进阶 ",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!
高级前端进阶
前不久关于core-js作者的事情在科技圈闹得沸沸扬扬,具体可以阅读我的文章《前端地震!core-js作者放弃开源?》,今天将带着大家一起细数前端周均下载百万、甚至千万级别的Polyfill。话不多说,直接开始!什么是polyfill?
polyfill 是通过修补 API 来添加缺失功能的代码,典型的例子是通过polyfill在旧浏览器中添加新功能。 例如,Modernizr 检测浏览器功能并使用一组 polyfill 来启用旧浏览器中的浏览器功能,或者提供尚不支持的功能的补丁。
下面代码是笔者用于处理Android 4.4以下浏览器webview不支持部分功能的代码,分别添加了url-polyfill(可以使用new URL)、babel-polyfill、es5-shim(部分代码兼容)这3个polyfill:
这种解决方案的问题是:因为它们是修补缺失的API方法,所以特点是污染全局作用域。至于如何解决,以前写过一篇文章《前端 Polyfill、Ponyfill、Prollyfill 傻傻分不清楚?》单独论述过,本文将重点关注在前端的热门polyfill上。core-js
什么是core-js?
Core-js是JavaScript 的模块化标准库。 包括 ECMAScript 的 Polyfill 到 ES2023,主要囊括:Promise、Symbol、Collections、 Iterators、 Typed Arrays,、类型数组以及诸多其他特性、ECMAScript 提案、一些跨平台的 WHATWG/W3C 特性和提案等等。
将上面的简短介绍拆开来看,主要包括以下核心特性:Core-js是JavaScript 标准库中最流行和最通用的 polyfill:它为最新的 ECMAScript 标准和提案提供支持,从古老的 ES5 功能到迭代器等前沿功能,以及与 ECMAScript 密切相关的 Web 平台功能,如 structuredClone等等。Core-js是最复杂和最全面的 polyfill 项目: core-js 包含大约 5000 个复杂程度不同的 polyfill 模块(NPM文件数量显示为3331 ),从 Object.hasOwn 或 Array.prototype.at 到 URL、Promise 或 Symbol等等。Core-js最大限度地模块化:可以允许开发者仅加载需要的功能,而且可以不污染全局命名空间。Core-js不是一个框架,其专为与工具集成而设计,并提供了为此所需的一切。例如:babel-polyfill、@babel/preset-env、@babel/transform-runtime,类似的 SWC 功能都基于 core-js,而且最重要的是开发无感,开箱即用。
比如通过如下方式引入core-js将不会污染全局变量:import Promise from "core-js-pure/actual/promise"; import Set from "core-js-pure/actual/set"; import Iterator from "core-js-pure/actual/iterator"; import from from "core-js-pure/actual/array/from"; import flatMap from "core-js-pure/actual/array/flat-map"; import structuredClone from "core-js-pure/actual/structured-clone"; Promise.resolve(42).then(it => console.log(it)); // => 42 from(new Set([1, 2, 3]).union(new Set([3, 4, 5]))); // => [1, 2, 3, 4, 5] flatMap([1, 2], it => [it, it]); // => [1, 1, 2, 2] Iterator.from(function * (i) { while (true) yield i++; }(1)) .drop(1).take(5) .filter(it => it % 2) .map(it => it ** 2) .toArray(); // => [9, 25] structuredClone(new Set([1, 2, 3])); // => new Set([1, 2, 3])
目前Core-js的NPM周下载量稳定在34138K,Github上star超过21.7K,fork达到了1.6K,有超过13,642K的项目使用它,超过115+代码贡献者。es5-shim
es5-shim.js 和 es5-shim.min.js 用来为 JavaScript 上下文打补丁以包含所有 EcmaScript 5 方法,这些方法可以用遗留 JavaScript 引擎模拟。 注意:由于 es5-shim.js 是为了给原生 Javascript 引擎打补丁,所以它应该是最先加载的库。Array.prototype.every (standalone shim) Array.prototype.filter (standalone shim) Array.prototype.forEach (standalone shim) Array.prototype.indexOf (standalone shim) // 更多数组方法 Date.now Date.prototype.toJSON // 更多Date方法 Number.prototype.toExponential (standalone shim) Number.prototype.toFixed Number.prototype.toPrecision // 更多Number方法 String.prototype.split (standalone shim) String.prototype.trim (standalone shim) String.prototype.lastIndexOf (standalone shim) String.prototype.replace // 更多String方法 Error.prototype.toString Error.prototype.name Error.prototype.message // 更多Error方法
es5-sham.js 和 es5-sham.min.js 为 其他 ES5 方法打补丁,比如:Object.create Object.getPrototypeOf Object.getOwnPropertyNames Object.isSealed Object.isFrozen Object.isExtensible Object.getOwnPropertyDescriptor Object.defineProperty // 更多其他方法
目的是为了允许将代码写入 ES5 而不会在旧引擎中导致运行时错误。 在许多情况下,这意味着这些打补丁的方法会导致许多 ES5 方法在低版本浏览器上默默地执行失败。 同时需要注意的是:es5-sham.js 需要 es5-shim.js 才能正常工作。
在浏览器项目中使用 ES 兼容性垫片的示例代码如下:
目前es5-shim的NPM周下载量稳定在3443K,Github上star超过7.1K,fork达到了1K。@babel/polyfill
Babel 默认只转换新的 JavaScript 句法,例如箭头函数、扩展运算符等,而不会转换新的 API,像是Set、Maps、Iterator、Generator 、Symbol、Reflect 等全局对象,以及一些定义在全局对象上的方法都不会进行转译。如果想使用这些新的对象和方法,则需要为当前环境提供一个 polyfill 垫片。
例如: ES6 在 Array 对象上有一个新增的 Array.from 方法,因为这个方法是全局对象上的方法,所以 Babel 就不会对这个方法进行转译。如果想让这个方法运行,就要使用 @babel/polyfill 为当前环境提供一个垫片。
需要首先安装:npm install --save @babel/polyfil
安装好后就可以在程序入口文件的顶部引用 @babel/polyfil:: import "@babel/polyfill" [].findIndex("babel")
babel-polyfill 解决了 Babel 不转换新 API 的问题,但是直接在代码中插入帮助函数,会导致污染了全局环境,并且不同的代码文件中包含重复的代码,导致编译后的代码体积变大。虽然这对于应用程序或命令行工具来说可能是好事,但如果已有代码打算提供给其他人使用的库,可能会有问题。
需要注意的是从 Babel 7.4.0 开始,不再推荐使用 @babel/polyfill 包,而是直接使用 core-js/stable 和 regenerator-runtime/runtime,如下所示: import "core-js/stable"; import "regenerator-runtime/runtime";
目前@babel/polyfil的NPM周下载量稳定在1572K,Github上star超过42K,fork达到了5.6K,有超过8K的项目使用它。es6-promise-polyfill
这是 ES6 Promise 的 polyfill,该实现基于 Jake Archibald 实现的 rsvp.js 子集。这个库的 主要目标是:Promise实现应该与浏览器的原生实现保持一致,并且尽可能小。 所以它只是 ES6 Promise 规范的严格 polyfill,仅此而已。
它通过了 Promises/A+ 测试套件和 rsvp.js 测试套件,体积最小 2.6KB( 1KB+gzip)。默认使用setImmediate(如果可用),或者回退使用 setTimeout。const Promise = require("es6-promise-polyfill").Promise; const promise = new Promise(...);
或者使用CDN方式加载:
目前es6-promise-polyfill的NPM周下载量稳定在32K。es6-promise
这是 ES6 Promise 的 polyfill,实现是由 @jakearchibald 提取的 rsvp.js 的一个子集。使用这个库很简单,可以通过CDN的方式引入:
或者直接使用模块化方法:const Promise = require("es6-promise").Promise;
如果要填充全局环境(在 Node 中或通过 CommonJS 在浏览器中),请使用以下代码片段: require("es6-promise").polyfill(); // 或者 require("es6-promise/auto");
请注意,上面代码没有将 polyfill() 的结果分配给任何变量, polyfill() 方法将在调用时自动修补全局环境(在本例中为 Promise 名称)。
目前es6-promise的NPM周下载量稳定在8000K,Github上star超过7.3K,fork达到了637+,有超过3008K的项目使用它。promise-polyfill
用于浏览器和Node环境的轻量级 ES6 Promise polyfill, 严格遵守浏览器规范。 它是一个完美的 polyfill IE 或任何其他不支持原生Promise的浏览器。同时,promise-polyfill非常轻巧,压缩后 < 1kb 。
可以通过CDN方式引用:
以上代码执行时候,如果浏览器没有 window.Promise,将会自动设置一个全局 Promise 对象。如果本机 Promise 不存在时候,想添加一个全局 Promise 对象(Node或浏览器),也可以使用如下模块化方法:import "promise-polyfill/src/polyfill";
如果不想影响全局环境(有时称为 ponyfill),可以导入基本模块。 import Promise from "promise-polyfill";
默认情况下,promise-polyfill 使用 setImmediate,如果不支持这个方法,则回退到 setTimeout 以异步执行。 因此,如果浏览器不支持 setImmediate(IE/Edge 是唯一支持 setImmediate 的浏览器),您可能会遇到性能问题。但是,可以使用 setImmediate polyfill 来解决这个问题。import Promise from "promise-polyfill/src/polyfill"; import setAsap from "setasap"; Promise._immediateFn = setAsap;
目前promise-polyfill的NPM周下载量稳定在2338K,Github上star超过2.1K,fork达到了300+,有超过756K的项目使用它。promise
这是 Promises 的简单实现,它是一组 ES6 Promises 的超集,旨在提供可读、高性能的代码,并仅提供当今使用 promises 绝对必要的扩展。
注意:Promise通过下划线 (_) 前缀属性公开内部结构。
可以通过CDN方式引用: //请注意,es5-shim 必须在此库之前加载,以支持 IE9 之前的浏览器。
或者也可以使用模块方法:const Promise = require("promise"); const promise = new Promise(function (resolve, reject) { get("http://www.google.com", function (err, res) { if (err) reject(err); else resolve(res); }); });
promise库本身提供了诸如:Promise.resolve、Promise.reject、Promise.all、Promise.any、Promise.allSettled、Promise.denodeify、Promise.race等常见方法。比如下面是Promise.all的使用示例:Promise.all([Promise.resolve("a"), "b", Promise.resolve("c")]) .then(function (res) { assert(res[0] === "a") assert(res[1] === "b") assert(res[2] === "c") })
目前promise的NPM周下载量稳定在11704K,Github上star超过2.5K,fork达到了310+,有超过9560K的项目使用它。url-search-params-polyfill
这是 JavaScript 的 URLSearchParams 类的 polyfill 库。具有以下明显特征:实现了 MDN 文档中的所有功能。可用于浏览器和 Node.js环境检测浏览器是否完全支持 URLSearchParams 并扩展它兼容IE8及以上版本
可以通过如下方式导入使用:import "url-search-params-polyfill"; // Babel 和 ES2015+ require("url-search-params-polyfill"); //es5导入
开发者可以从字符串或对象实例化 URLSearchParams 的实例: // new an empty object var search1 = new URLSearchParams(); // from a string var search2 = new URLSearchParams("id=1&from=home"); // from an object var search3 = new URLSearchParams({ id: 1, from: "home" }); // from location.search, will remove first "?" automatically var search4 = new URLSearchParams(window.location.search); // from anther URLSearchParams object var search5 = new URLSearchParams(search2); // from a sequence var search6 = new URLSearchParams([["foo", 1], ["bar", 2]]);
然后调用append、delete、get、getAll、has、set、toString、sort、forEach、keys、values、for..of等方法对URLSearchParams进行更改。 url-polyfill
Polyfill URL类 和 URLSearchParams类 以匹配最新的 WHATWG 规范。这个库在大多数用例中保持兼容,但不是 100%(如 unicode 字符、punycode 等),支持 IE 10+ 以上环境。
可以通过如下方式安装:npm i url-polyfill --save
安装后即可在代码中直接使用:const url = new URL("https://www.example.com/?fr=yset_ie_syc_oracle&type=orcl_hpset#page0"); url.searchParams.append("page", 0); console.log(url.toString()); // print: "https://www.example.com/?fr=yset_ie_syc_oracle&type=orcl_hpset&page=0#page0"
目前url-polyfill的NPM周下载量稳定在285K,Github上star超过300+,fork达到了60+。util.promisify
Node版本 < v8 的 util.promisify 的 Polyfill,但是Node v8.0.0 已经添加了对内置 util.promisify 的支持,无需再使用这个库。
可以直接使用:const promisify = require("util.promisify"); // Use `promisify` just like the built-in method on `util`
或者shim方式引入:require("util.promisify/shim")(); // `util.promisify` is now defined const util = require("util"); // Use `util.promisify`
这个包需要一个原生的 ES5 环境,并且 Promise 在全局范围内可用,否则会抛出错误。 目前util.promisify的NPM周下载量稳定在13756K。本文总结
本文主要和大家介绍 Polyfill是什么,前端最火的10大Polyfill,以及如何使用。因为篇幅有限,文章并没有就每一个Polyfill过多展开,如果有兴趣,可以直接在我主页继续阅读,但是文末的参考资料提供了大量优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!
参考资料
https://www.npmjs.com/package/core-js
https://www.npmjs.com/package/es5-shim
https://www.npmjs.com/package/babel-polyfill
https://www.npmjs.com/package/es6-promise
https://www.npmjs.com/package/promise-polyfill
https://www.npmjs.com/package/promise
https://www.npmjs.com/package/url-search-params-polyfill
https://www.npmjs.com/package/url-polyfill
https://www.npmjs.com/package/util.promisify
https://www.npmjs.com/package/es6-promise-polyfill