Як аудит залежностей із Baseline допомагає відправляти менше JavaScript
Матеріал розбирає підхід Jad Joubran із Smashing Magazine до аудиту залежностей у JavaScript‑проєктах за допомогою Baseline. Ідея проста: браузери вже вміють багато того, для чого колись ставили бібліотеки, тож частину кілобайтів можна безпечно «повернути платформі».
Що таке Baseline і навіщо він у цьому підході
Baseline — це ініціатива WebDX Community Group, яка описує, наскільки безпечно використовувати конкретну можливість веб‑платформи у сучасних браузерах (Chrome, Edge, Firefox, Safari). Кожна фіча має один зі статусів:
- Limited availability — ще не у всіх рушіях; без фолбеків покладатися не можна.
- Baseline Newly available — уже є в усіх основних рушіях, але ще не пройшло 30 місяців; на старіших девайсах може не працювати.
- Baseline Widely available — пройшло 30 місяців з появи в усіх рушіях; вважається безпечним для широкого використання.
Саме різниця між Newly та Widely визначає, чи бібліотеку можна просто видалити, чи потрібно:
- перевірити свою аудиторію (аналітика,
browserslist), - або додати фіче‑чек і фолбек.
Статус будь-якої можливості можна подивитися на webstatus.dev або на MDN (бейдж Baseline на сторінці фічі), а також програмно через пакет web-features.
Рамка рішень: три питання перед видаленням будь-якої бібліотеки
Автор пропонує не «зривати пластир» відразу, а проганяти кожну потенційну заміну через невелику рамку з трьох питань:
- Чи безпечна нативна фіча для моєї аудиторії?
Якщо вона Baseline Widely available — майже завжди «так». Якщо Newly available — дивитися на свою статистику браузерів: внутрішній B2B‑дешборд і публічний сайт з довгим «хвостом» старих Android — це різні умови. - Яка фактична «ціна» заміни?
Іноді для нативної фічі ще потрібен поліфіл. Якщо поліфіл важчий за поточну бібліотеку, ви лише збільшуєте бандл (як у випадку з Temporal). - Чи реально платформа покриває ваш кейс?
Бібліотеки часто роблять більше, ніж виглядає на перший погляд. Наприклад,axios— це не тількиfetchз JSON, а ще й інтерцептори, тайм‑аути, ретраї, обробка помилок. Потрібно звірити, що саме ви використовуєте.
Усі подальші «кластери» бібліотек у матеріалі — це по суті багаторазове застосування цих трьох питань до різних типів залежностей.
Кластер 1: інтернаціоналізація — найбільший «легкий» виграш
У сфері форматування дат, чисел і списків браузер уже дає багато готових рішень через простір імен Intl. Частину популярних бібліотек можна просто прибрати:
timeago.js→Intl.RelativeTimeFormat(Widely)pluralize→Intl.PluralRules(Widely)numeral→Intl.NumberFormat(Widely)humanize-duration→Intl.DurationFormat(Newly)- «ручні» джойни списків →
Intl.ListFormat(Widely)
Відносний час замість timeago.js
Бібліотека timeago.js конвертує мітку часу у фразу на кшталт «3 hours ago». Це саме вміє Intl.RelativeTimeFormat — і вже Widely available.
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
rtf.format(-1, "day"); // "yesterday"
rtf.format(3, "hour"); // "in 3 hours"
rtf.format(-2, "week"); // "2 weeks ago"
Єдина додаткова частина, яку дає timeago.js, — автоматичний вибір одиниці («секунди», «хвилини», «дні»). У нативному підході це кілька рядків арифметики, які легко сховати в утиліту — після цього потреба у самій бібліотеці зникає.
Числа, валюта та списки
Intl.NumberFormat закриває більшість завдань форматування чисел: тисячні розділювачі, валюта, відсотки, компактний запис.
new Intl.NumberFormat("en-US").format(1234567.89);
// "1,234,567.89"
new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(
1234.5,
);
// "$1,234.50"
new Intl.NumberFormat("en", { notation: "compact" }).format(1200000);
// "1.2M"
Intl.ListFormat (Widely) акуратно вирішує задачу перетворення масиву в речення з потрібними комами й «and»/«та» — без ручних умов по довжині масиву.
const lf = new Intl.ListFormat("en", { style: "long", type: "conjunction" });
lf.format(["Alice", "Bob", "Carol"]);
// "Alice, Bob, and Carol"
Обмеження Intl.DurationFormat
Intl.DurationFormat дозволяє форматувати тривалість («1 hour, 30 minutes») і функціонально замінює humanize-duration:
const df = new Intl.DurationFormat("en", { style: "long" });
df.format({ hours: 1, minutes: 30 });
// "1 hour, 30 minutes"
Але на момент опису цей API — Baseline Newly available. Для широкої публічної аудиторії це ще «питання №1»: потрібно або перевірити, який відсоток користувачів на сучасних браузерах, або додати фолбек. Для внутрішніх інструментів на сучасних браузерах — уже сміливо можна використовувати.
У сумі набір «дато‑числових» бібліотек у типовому проєкті легко набігає до ~14 KB (мінімізовано й gzip), більша частина яких сьогодні закривається нативними Intl‑API.
Кластер 2: HTTP‑клієнти та межі fetch
Другий кластер — мережеві бібліотеки, насамперед axios і superagent. Для більшості запитів достатньо fetch + AbortController, які вже Widely available.
Ціна заміни тут — одна додаткова операція (res.json()) й потреба явно обробляти помилки.
Тайм‑аути та скасування
axios надає timeout-опцію, для fetch аналог — AbortSignal.timeout():
Де fetch ще не дорівнює axios
За рамкою «питання №3» автор підкреслює конкретні прогалини:
- HTTP‑помилки не відхиляють проміс.
fetchрезолвиться й для 404/500; потрібно перевірятиres.okвручну.axiosкидає помилку для всіх не‑2xx. - Немає інтерцепторів. Якщо ви підсаджуєте токени, логіку оновлення токена чи глобальну обробку 401 в інтерцептор — доведеться обгорнути
fetchвласним класом/функцією. - Немає вбудованих ретраїв. Плагіни до
axiosроблять ретраї з коробки; ізfetchце вже ваша логіка. - Немає підтримки прогресу завантаження файлів. Для завантаження з прогрес‑баром досі часто вигідніше залишити бібліотеку.
Автор статті згадує, що в навчальних проєктах (наприклад, Learn JavaScript) команда розв’язує завдання інтерцепторів власною обгорткою над fetch і успішно застосовує це для великої кількості користувачів. Це приклад, а не універсальна рекомендація.
Практичний висновок: якщо ви використовуєте HTTP‑клієнт тільки для простих GET/POST — заміна на тонкий шар над fetch дає економію приблизно 17–19 KB gzip. Якщо активно покладаєтесь на інтерцептори, прогрес та інші можливості, варто або залишити бібліотеку, або уважно оцінити обсяг коду для власної реалізації.
Кластер 3: UI‑примітиви — діалоги, поповери, тултіпи
Третій великий напрям — мінімальні UI‑бібліотеки: модальні діалоги, тултіпи, поповери, фокус‑трап, блокування скролу фону. Автор показує, як їх заміняють:
<dialog>+ CSS для блокування скролу,- Popover API,
- CSS anchor positioning.
Модальні вікна на <dialog>
Багато кілобайтів коду в модальних бібліотеках йде на доступність: фокус у модалі, закриття по Escape, повернення фокусу назад, топ‑лейєр, backdrop. Усе це дає нативний елемент <dialog> (Widely available):
Метод showModal() забезпечує фокус‑трап і робить інший контент сторінки неактивним для навігації. Додатково є псевдоелемент ::backdrop для стилізації підкладки.
Окремо бібліотеки часто додають блокування скролу body. Нині це вирішується однією декларацією CSS:
Важлива деталь: використовується :modal, а не [open], щоб не блокувати скрол до того, як діалог стане справді модальним.
Popover API та anchor positioning замість тултіп‑бібліотек
Для легких поповерів (меню, підказки, невеликі панелі) Popover API дає:
- перемикання стану по кліку без JS,
- закриття по кліку поза елементом,
- обробку Escape,
- рендеринг у top layer.
Цей API — Baseline Newly available, тож для публічних проєктів варто перевірити аудиторію або додати фолбек.
Друге завдання тултіп‑бібліотек — позиціонування відносно тригера, з урахуванням обрізання вьюпортом. Цю роль тепер бере на себе CSS anchor positioning, де тригер оголошується як «якір», а поповер прив’язується до нього через position-anchor і position-area. Ця можливість теж Newly available, і тут ще важливіше закласти простий фолбек.
У сумі модальні, тултіпні та скрол‑блокувальні бібліотеки легко додають ~24 KB gzip, які в сучасних браузерах можна замінити нативними можливостями з кращими дефолтами з точки зору доступності.
Кластер 4: точкові утиліти Lodash, які вже є в платформі
Навіть якщо проєкт давно не тягне весь lodash, окремі його модулі — lodash.clonedeep, lodash.groupby та інші — досі часто сидять у package.json. Частина таких утиліт тепер має прямі відповідники в стандартній бібліотеці.
Групування масивів: Object.groupBy
lodash.groupby перетворює масив об’єктів на об’єкт, де ключі — значення якоїсь властивості. Аналог — Object.groupBy:
Є і Map.groupBy для випадків, коли потрібен саме Map. Обидві фічі Baseline Newly available, тож для широкої аудиторії потрібно зважити підтримку старих браузерів.
Глибоке клонування: structuredClone
lodash.clonedeep дає глибоку копію об’єкта. Сьогодні цю роль відіграє structuredClone (Widely available):
structuredClone коректно працює з Date, Map, Set, ArrayBuffer та циклічними посиланнями. Обмеження важливі для «питання №3»: він не клонує функції, DOM‑вузли та зберігає не всі прототипи класів. Для «чистих даних» це повноцінна заміна, але для об’єктів зі складною поведінкою — не завжди.
Операції над множинами: методи Set
Для задач типу union/intersection/difference більше не потрібні утиліти Lodash: Set отримав відповідні методи (Newly available):
До набору входять також symmetricDifference, isSubsetOf, isSupersetOf, isDisjointFrom. Це знімає потребу в окремих дрібних пакетах на кшталт «set‑operations‑helpers».
Водночас деякі частини Lodash усе ще мають сенс: наприклад, debounce і throttle не мають нативних аналогів, і точкове підключення lodash.debounce цілком виправдане.
Кластер 5: Temporal як приклад, коли не варто поспішати
На відміну від попередніх кластерів, з Temporal автор демонструє ситуацію, коли рамка трьох питань підказує «почекати».
Temporal — новий стандарт для роботи з датами й часом у JavaScript (ES2026), що суттєво кращий за Date з точки зору моделі даних і роботи з часовими зонами. Firefox і Chrome вже його реалізували, Safari — на підході, але:
- Temporal ще не Baseline, має статус limited availability;
- щоб використовувати Temporal всюди, потрібен поліфіл.
Офіційний поліфіл @js-temporal/polyfill важить приблизно 44 KB gzip, «полегшений» без BigInt — ~19 KB. Для порівняння, популярні бібліотеки дат типу dayjs займають близько 3 KB gzip.
Якщо замінити dayjs на Temporal із поліфілом прямо зараз, бандл збільшиться — тобто «питання №2» (ціна заміни) тут провалюється. Навіть попри те, що «питання №3» (функціональність) — явно на користь Temporal.
Рекомендація автора: залишити легку бібліотеку дат, поки Temporal не стане Baseline. Після цього можна буде підключати поліфіл умовно (для старих браузерів) або зовсім від нього відмовитися.
Як самостійно провести аудит свого package.json
Нарешті, автор формулює покрокову процедуру аудиту, яку можна повторювати раз на квартал.
Крок 1. Витягнути продакшен‑залежності
Спершу потрібно подивитися, що насправді їде в продакшен:
Крок 2. Оцінити «вагу» кожної залежності
- Для швидкої оцінки окремого пакета — bundlephobia.com (мінімізований і gzip‑розмір).
- Для реальної картини у вашому бандлі — аналізатор на зразок
npx source-map-explorerабоnpx vite-bundle-visualizer.
Крок 3. Знайти нативну заміну й перевірити Baseline
Для кожної «важкої» або очевидно утилітарної бібліотеки:
- з’ясувати, чи існує нині нативний еквівалент;
- подивитися його Baseline‑статус через webstatus.dev або MDN.
Крок 4. Прогнати через три запитання
Далі для кожної кандидатки на видалення відповісти на три ключові питання (аудиторія, вартість, покриття кейсу). Більшість рішень впираються у:
- статус фічі + ваш
browserslist(питання 1), - фактичний розмір бібліотеки/поліфіла (питання 2),
- реальний набір можливостей, які ви використовуєте (питання 3).
Крок 5. М’які заміни через progressive enhancement
Для Widely available‑фіч — можна просто замінити бібліотеку на нативний API. Для Newly available варто або впевнитися, що аудиторія — на сучасних браузерах, або зробити фолбек через фіче‑чек:
Це дозволяє відправляти менше коду тим, хто вже підтримує потрібні можливості, не ламаючи досвід для решти.
Підсумок підходу
Якщо скласти «кластери» разом, для типової середньої SPA автор оцінює потенційну економію в діапазоні 60–90 KB gzip:
- ≈14 KB — інтернаціоналізація,
- ≈17 KB — HTTP‑клієнт,
- ≈24 KB — UI‑примітиви (модалки, тултіп‑бібліотеки, фокус‑трап, блокування скролу),
- ≈8 KB і більше — Lodash‑утиліти (залежить від того, скільки функцій ви тягнете).
Ці цифри не універсальні — залежать від конкретних пакетів і способу імпорту. Але методика стабільна: раз на деякий час переглядати package.json, звірятися з Baseline і видаляти бібліотеки, за які платформа вже відповіла.
Практичний старт: обрати один із кластерів (наприклад, інтернаціоналізацію чи UI‑примітиви), подивитися власні залежності й перевірити, що з них уже можна замінити на нативні можливості без шкоди для вашої конкретної аудиторії.
Джерело: Jad Joubran — Smashing Magazine