JS?生態(tài)系統(tǒng)加速Polyfill函數(shù)使用實(shí)例探索
引言
長話短說:一大坨人氣爆棚的 npm 軟件包的依賴比它們需要的軟件包多 6-8 倍。其中大部分都是多余的 polyfill
(功能補(bǔ)?。?,這是 node_modules
文件夾過度肥胖的關(guān)鍵原因之一。eslint
生態(tài)系統(tǒng)似乎是最倒霉的倒霉蛋。
本期《前端翻譯計(jì)劃》共享的是“加速 JS 生態(tài)系統(tǒng)系列博客”,包括但不限于:
- PostCSS,SVGO 等等
- 模塊解析
- 使用 eslint
- npm 腳本
- draft-js emoji 插件
- polyfill 暴走
- 桶裝文件崩潰
- Tailwind CSS
Polyfill 暴走
我們研究了運(yùn)行時(shí)性能,私以為瞄一下 Node 模塊安裝時(shí)間會很有趣。關(guān)于各種算法優(yōu)化,或使用更高性能的系統(tǒng)調(diào)用,已經(jīng)寫了一大坨博客,但為什么我們首先會遭遇此問題呢?為什么每個(gè) node_modules
文件夾都過度么肥胖?所有這些依賴來自何方?
這一切都始于我有一個(gè)朋友注意到,它的項(xiàng)目依賴樹有些奇葩。每當(dāng)它更新一個(gè)依賴時(shí),它就會引入幾個(gè)新的依賴,且隨著后續(xù)每次更新,依賴的總數(shù)與日俱增。
├─┬ arraybuffer.prototype.slice 1.0.2 │ └─┬ define-properties 1.2.1 │ └── define-data-property 1.1.0 ├─┬ function.prototype.name 1.1.6 │ └─┬ define-properties 1.2.1 │ └── define-data-property 1.1.0 ├─┬ globalthis 1.0.3 │ └─┬ define-properties 1.2.1 │ └── define-data-property 1.1.0 ├─┬ object.assign 4.1.4 │ └─┬ define-properties 1.2.1 │ └── define-data-property 1.1.0 ├─┬ regexp.prototype.flags 1.5.1 │ ├─┬ define-properties 1.2.1 │ │ └── define-data-property 1.1.0 │ └─┬ set-function-name 2.0.1 │ └── define-data-property 1.1.0 ├─┬ string.prototype.trim 1.2.8 │ └─┬ define-properties 1.2.1 │ └── define-data-property 1.1.0 ├─┬ string.prototype.trimend 1.0.7 │ └─┬ define-properties 1.2.1 │ └── define-data-property 1.1.0 └─┬ string.prototype.trimstart 1.0.7 └─┬ define-properties 1.2.1 └── define-data-property 1.1.0
平心而論,一個(gè)包可能依賴額外的依賴?yán)碛沙浞?。在這里,我們開始注意到一個(gè)模式:新的依賴都是 JS 函數(shù)的 polyfill
,這些函數(shù)一直被普遍支持。舉個(gè)栗子,Object.defineProperties
方法是作為 2013 首個(gè)公共 Node 0.10.0 版本的一部分。甚至 IE9(Internet Explorer 9)也支持它。雖然但是,一大坨軟件包依賴 polyfill
來實(shí)現(xiàn)。
在引入 define-properties
的各種包中,有 eslint-plugin-react
。它引起了我的注意,因?yàn)樗?React 生態(tài)系統(tǒng)中人氣爆棚。為什么它會為 Object.defineProperties
引入 polyfill
?這在所有 JS 引擎都內(nèi)置了。
沒有 polyfill 的 polyfill
閱讀包的源碼發(fā)現(xiàn)了更奇葩的事情:polyfill
函數(shù)直接導(dǎo)入和調(diào)用,而不是在運(yùn)行時(shí)環(huán)境中修補(bǔ)缺失的功能。polyfill
的重點(diǎn)是對用戶代碼透明。它應(yīng)該檢查需要打補(bǔ)丁的函數(shù)或方法是否可用,并且當(dāng)且僅當(dāng)需要時(shí)才添加它。當(dāng)不需要 polyfill
時(shí),它不直接躺平。對我而言奇怪的是,這些函數(shù)像庫中的函數(shù)一樣直接使用。
// 為何 defined 函數(shù)直接導(dǎo)入? var define = require('define-properties') // 更糟糕的是,為何它被直接調(diào)用了? define(polyfill, { getPolyfill: getPolyfill, implementation: implementation, shim: shim })
相反,它們應(yīng)該直接調(diào)用 Object.defineProperties
。polyfill
的重點(diǎn)是環(huán)境補(bǔ)丁,而不是直接調(diào)用。將其與 Object.defineProperties
的 polyfill
比較應(yīng)該是什么是這樣:
// 檢查當(dāng)前環(huán)境是否已經(jīng)支持 Object.defineProperties // 如果是,那我們直接躺平就歐了 if (!Object.defineProperties) { // Patch in Object.defineProperties here }
諷刺的是,使用 define-properties
的高頻熱點(diǎn)是在其他 polyfill
內(nèi)部,它們加載了更多 polyfill
。define-properties
包依賴更多的依賴,而不僅僅是它本身。
var keys = require('object-keys') // ... var defineDataProperty = require('define-data-property') var supportsDescriptors = require('has-property-descriptors')() var defineProperties = function (object, map) { // ... } module.exports = defineProperties
在 eslint-plugin-react
內(nèi)部,該 polyfill
通過 Object.entries()
的 polyfill
加載來處理其配置:
const fromEntries = require('object.fromentries') // <- 為何它直接就使用了? const entries = require('object.entries') // <- 為何它直接就使用了? const allRules = require('../lib/rules') function filterRules(rules, predicate) { return fromEntries(entries(rules).filter(entry => predicate(entry[1]))) } const activeRules = filterRules(allRules, rule => !rule.meta.deprecated)
整理小結(jié)
在撰寫本文時(shí),安裝 eslint-plugin-react
總共需要引入高達(dá) 97 個(gè)依賴。我很好奇其中有多少是 polyfill
,并開始在本地將它們逐個(gè)打補(bǔ)丁。完成所有操作后,依賴總數(shù)斷崖式下跌至 15 個(gè)。原來的 97 個(gè)依賴項(xiàng)中,有 82 個(gè)不再需要。
巧合的是,在各種 eslint
預(yù)設(shè)中同樣流行的 eslint-plugin-import
也顯示出類似問題。安裝后,node_modules
文件夾將塞滿 87 個(gè)軟件包。經(jīng)過另一次本地清理之后,我將這個(gè)數(shù)字減少到了 17 個(gè)。
填滿每個(gè)人的磁盤空間
現(xiàn)在您可能想知道自己是否深受其害。我進(jìn)行了快速搜索,基本上您能想到的所有人氣爆棚的 eslint
插件或預(yù)設(shè)都難逃毒手。出于某種原因,這整個(gè)考驗(yàn)讓我想起了不久前此行業(yè)發(fā)生的 is-even/is-odd
事件。
擁有如此多的依賴使得審核項(xiàng)目的依賴難上加難。這也浪費(fèi)空間。舉個(gè)栗子:刪除項(xiàng)目中的所有 eslint
插件和預(yù)設(shè)就刪除了 220 包。
pnpm -r rm eslint-plugin-react eslint-plugin-import eslint-import-resolver-typescript eslint-config-next eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-prettier prettier eslint-config-prettier eslint-plugin-react-hooks Scope: all 8 workspace projects . | -220 ----------------------
也許我們一開始就不需要那么多依賴。我想起了 Erlang 編程語言的創(chuàng)建者的這句精彩的名言:
您只想要一根香蕉,但您得到的是一只拿著香蕉的大猩猩和整個(gè)叢林。
免責(zé)聲明
本文屬于是語冰的直男翻譯了屬于是,略有刪改,僅供粉絲參考,英文原味版請傳送 Speeding up the JavaScript ecosystem - Polyfills gone rogue[1]
https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-6
以上就是JS 生態(tài)系統(tǒng)加速Polyfill函數(shù)使用實(shí)例探索的詳細(xì)內(nèi)容,更多關(guān)于JS Polyfill函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
js+html實(shí)現(xiàn)周歲年齡計(jì)算器
這篇文章主要為大家詳細(xì)介紹了js+html實(shí)現(xiàn)周歲年齡計(jì)算器的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06JavaScript學(xué)習(xí)筆記之取值函數(shù)getter與取值函數(shù)setter詳解
這篇文章主要介紹了JavaScript取值函數(shù)getter與取值函數(shù)setter,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08js中對函數(shù)設(shè)置默認(rèn)參數(shù)值的3種方法
這篇文章主要介紹了js中對函數(shù)設(shè)置默認(rèn)參數(shù)值的3種方法嗎,3種方法都具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2015-10-10ES6入門教程之Iterator與for...of循環(huán)詳解
最近在學(xué)習(xí)ES6,剛剛看到Iterator和for...of循環(huán)這一章,所以想要跟大家略微分享一下,下面這篇文章主要給大家介紹了關(guān)于ES6入門學(xué)習(xí)中Iterator與for...of循環(huán)的相關(guān)資料,不足之處還望大家多多指正,需要的朋友們可以參考學(xué)習(xí)。2017-05-05帶有定位當(dāng)前位置的百度地圖前端web api實(shí)例代碼
這篇文章主要介紹了帶有定位當(dāng)前位置的百度地圖前端web api實(shí)例代碼 的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06js操作輸入框提示信息且響應(yīng)鼠標(biāo)事件
注冊網(wǎng)站的輸入框就有默認(rèn)提示值,當(dāng)獲取鼠標(biāo)焦點(diǎn)的時(shí)候,默認(rèn)值被刪除,當(dāng)用戶沒輸入東西焦點(diǎn)離開的時(shí)候,又恢復(fù)默認(rèn)提示值2014-03-03