Dlaczego drzewa zależności npm rozrastają się: trzy kluczowe przyczyny
Drzewa zależności w projektach JavaScript często puchną z powodu przestarzałych pakietów, które dublują natywne możliwości platformy. To prowadzi do zbędnych kosztów ładowania, rozwiązywania wersji i luk w łańcuchu dostaw. Przyjrzyjmy się trzem typowym scenariuszom wyjaśniającym tę sytuację oraz sposobom ich naprawy.
Obsługa legacy-środowisk i specyfika realm
Wiele pakietów, takich jak is-string czy hasown, istnieje dla kompatybilności z archaicznymi środowiskami wykonawczymi, jak ES3 (IE6/7, wczesny Node.js). Implementują brakujące metody: Array.prototype.forEach, Array.prototype.reduce, Object.keys, Object.defineProperty.
Dodatkowe motywacje:
- Ochrona przestrzeni globalnej: Node importuje primordials (np.
Map), aby uniknąć awarii spowodowanych nadpisywaniem. Pakiety takie jakmath-intrinsicsponownie eksportująMath.*w podobnym celu. - Kompatybilność realm:
instanceofnie działa między iframe a rodzicem (różne konstruktoryRegExp). Alternatywą jestObject.prototype.toString.call(val) === '[object RegExp]', jak wchailubis-stringdlanew String(val).
const shebangRegex = /^#!(.*)/;
export default shebangRegex;
Dla większości middle/senior-developerów to przesada: współczesny Node (nie starszy niż 10 lat) i przeglądarki nie wymagają takich hacków. Zalecenie: sprawdzaj browserslist i usuwaj niepotrzebne polyfille.
Architektura atomowa: od teorii do problemów
Filozofia 'atomowych' pakietów zakłada podział na minimalne bloki do ponownego użycia. Przykład — graf zależności execa z pakietami takimi jak shebang-regex (5 linii kodu).
Typowe atomowe narzędzia:
arrify:Array.isArray(val) ? val : [val]slash: normalizacja ukośników w ścieżkachcli-boxes: JSON z granicami elementów UIpath-key:env['PATH'] || env['Path']onetime: jednorazowe wywołanie funkcjiis-wsl:process.platform === 'linux' && /microsoft/.test(os.release())is-windows:process.platform === 'win32'
W teorii to upraszcza budowanie CLI. W praktyce:
- Jednorazowość:
shebang-regexjest używany tylko przezshebang-command(ten sam autor);cli-boxes— tylko przezboxen/ink. - Duplikaty w hoisting: W
nuxtduplikują sięis-docker,is-stream,is-wsl(po 2 wersje). Wbudowany kod duplikuje się za darmo, bez npm-overhead. - Ryzyka łańcucha dostaw: Więcej pakietów — więcej luk. Kompromitacja jednego maintainera (jak w zeszłym roku) dotknęła setki zależności.
Rozwiązanie: inlining prostych funkcji, tree-shaking, pnpm ze strict hoisting.
Ponyfille, które przetrwały swoją epokę
Ponyfille (ponyfill) — to polyfille bez mutacji globali, importowane bezpośrednio. Przydatne dla bibliotek: @fastly/performance-observer-polyfill pozwala używać PerformanceObserver bez zmiany środowiska.
Problem: nie są usuwane po pojawieniu się natywnego wsparcia.
Przykłady przestarzałych ponyfilli (z cotygodniowymi pobraniami):
globalthis— dlaglobalThis(wsparcie od 2019, 49M downloads/week)indexof— dlaArray.prototype.indexOf(od 2010, 2.3M/week)object.entries— dlaObject.entries(od 2017, 35M/week)
W eslint-plugin-react połowa grafu to legacy-polyfille. Dla bibliotek: polegaj na consumer-side polyfillach (core-js). Sprawdzaj caniuse.com przed dodaniem.
Co jest ważne
- Optymalizacja legacy: 90% pakietów nie potrzebuje ES3/ES5 polyfilli; używaj
typeofzamiastis-string. - Atomowość szkodzi: Wbudowuj 5-liniowe funkcje, redukując duplikaty i powierzchnię ataku.
- Audyt ponyfilli: Usuwaj pakiety dla funkcji wspieranych >5 lat w target runtime.
- Narzędzia:
npm ls --depth=0,depcheck,npm-check-updatesdo analizy. - Inicjatywa e18e: Skupienie na czyszczeniu przestarzałych deps dla wydajności.
Praktyczne kroki do czyszczenia
- Uruchom
npm dedupeinpm prune. - Dodaj
overrideswpackage.jsondo wymuszonego hoistingu. - Używaj
esbuild/vitez tree-shaking. - Monitoruj rozmiar bundla przez
webpack-bundle-analyzer.
To zmniejszy rozmiar bundla o 20-50% bez utraty funkcjonalności.
— Editorial Team
Brak komentarzy.