Полка браузера: что брать из npm, когда платформы мало
Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.
Снимок: сентябрь 2026. Девять задач, которые на странице чаще всего отдают библиотеке, — и для каждой сначала то, что браузер умеет сам. Граница между «нужна библиотека» и «хватит десяти своих строк» проходит не по популярности имени, а по стене, в которую упирается своя сборка, и часть этих стен запускается прямо на странице. Версии и веса ниже — снимок на дату проверки; как проверить любую строку полки самому, разбирает статья «Живой ли пакет».
Полка устроена так же, как вся часть II: сначала то, что браузер даёт сам, и только за стеной — пакет. Ниже девять задач, и у каждой в своём разделе паспорт одного вида: когда библиотека не нужна, что умеет платформа и какой у этого статус Baseline, где в учебнике своя сборка, снимок версий с датой проверки и условие, при котором строку пора пересмотреть.
| Задача | Сначала | Если мало |
|---|---|---|
| даты | Intl.DateTimeFormat и Intl.RelativeTimeFormat | Temporal с полифилом или date-fns |
| проверка данных | встроенная проверка формы и свой разбор | Valibot, а для общего с сервером контракта Zod |
| утилиты | structuredClone, Object.groupBy, Set | es-toolkit |
| графики | polyline в SVG | Chart.js, для больших рядов uPlot |
| анимация | CSS и element.animate() | Motion |
| таблицы | <table> и методы массивов | TanStack Table |
| чужой HTML | textContent, а где есть — setHTML | DOMPurify |
| поповеры и подсказки | popover и <dialog> | Floating UI |
| IndexedDB | localStorage для пары строк | idb |
В таблицах-снимках есть столбец «Вес или цена». Вес — это сколько байт уедет пользователю, когда сборщик склеит модули в один файл, минификатор сожмёт код, а сервер отдаст его в gzip. Килобайт там — 1024 байта, склеивал esbuild 0.28.2, и мерился типичный импорт: в скобках записано, что именно импортировали, потому что другой набор импортов даёт другой вес. Как сборщик склеивает модули и что выбрасывает по дороге, разбирает глава «Сборка» части IV, начиная со статьи «Свой сборщик». Где вес не мерили, так и сказано, и названа цена другого рода — лицензия.
Библиотека на полке — не награда, а временное решение: у каждого раздела записано событие, после которого его пора переписать.
Мерило знакомо с первой статьи части: статусы Baseline Widely, Newly и Limited, подробно разобранные в прологе. Правило, что делать с каждым статусом, когда на кону пакет, записано в статье «Живой ли пакет», в вопросе 0, — здесь его не повторяем и добавляем одно браузерное уточнение. Safari на iPhone обновляется только вместе с iOS, а старые телефоны на Android со временем перестают получать свежий Chrome. Поэтому Newly на этой полке — всегда с запасным путём. Статусы взяты из web-features 3.38.0 — набора данных, из которого берутся отметки Baseline.
Как узнать, что умеет браузер у читателя, разбиралось там же: спросить саму возможность, а не имя браузера. Спросим браузер, в котором вы это читаете, о пяти вещах с этой полки. Прогноз: что он ответит?
Temporal, ни setHTML, и эти две строки там ответят «нет»; сам пример в Safari мы не запускали.Вывод зависит от вашего браузера, и в этом смысл примера: одинакового ответа для всех здесь нет и быть не может.
Строка про anchor positioning учит второму: «есть» — ещё не «можно». CSS.supports проверяет одно свойство в одном браузере, а Baseline оценивает возможность целиком, со всеми её свойствами, во всех основных браузерах. Целиком anchor positioning, по web-features 3.38.0, пока не поддерживает ни один браузер, поэтому в разделе о поповерах она проходит как не Baseline. Проба решает, какой путь включить у этого читателя. Нужен ли запасной путь вообще, решает статус.
Когда возможности нет, путей два, и стареют они по-разному. Полифил дописывает браузеру недостающий стандарт: строка import "temporal-polyfill/global" ставит глобальный Temporal, и дальше код пишется так, будто браузер умеет его сам. Когда Temporal появится в Safari, эту строку удалят, а остальной код не тронут. Библиотека вроде date-fns — не стандарт, а свои функции, и её вызовы останутся в каждом файле, где считают даты. Поэтому там, где стандарт уже в пути, полку открывает полифил.
И правило о том, как стареет сама полка. «Платформа умеет» почти не стареет: возможности в браузерах только прибавляются. Стареет «пока не умеет»: сегодня в Safari нет Temporal, через год он может там появиться, и тогда раздел о датах будет врать. Поэтому каждое такое утверждение записано на полке вместе с условием пересмотра — строкой «Пересмотреть, если…» в конце раздела.
Спрашивайте возможность, а не имя браузера. А «пока не умеет» записывайте вместе с условием, при котором проверить снова.
Не нужна, если дату надо только показать: хватит Intl. Платформа: Intl.DateTimeFormat — часть Intl, Widely; Intl.RelativeTimeFormat — Widely с 16.03.2023; Intl.ListFormat — Widely с 26.10.2023; Intl.DurationFormat — Newly с 04.03.2025. Temporal не Baseline: он есть в Chrome и Edge с версии 144 и в Firefox с 139, а в Safari его нет.
Своя сборка уже была: показ даты через Intl.DateTimeFormat и сеттеры, которые переносят дату через границу месяца. В той статье перечислено, ради чего берут библиотеку: разбор строк, «через два месяца», чужие часовые пояса, «3 дня назад». Часть этого списка Intl закрывает сам. Пример показывает время в двух поясах, «вчера», цену в рублях и список. Прогноз: какое время будет во Владивостоке и что Intl поставит между «деньги» и «списки»?
Оговорка к этому выводу: снят он в Chrome 152, Firefox 155 дал то же самое, а на старых Android и iOS, где и данные CLDR старые, «г.», пробелы и порядок слов могут выйти иначе — такие движки мы не проверяли.
Стена — арифметика. «31 января плюс месяц» Date превращает в 3 марта, и Intl здесь не поможет: он только показывает. Вот та же операция в date-fns, в Temporal из полифила и в голом Date:
date-fns и Temporal упираются в последний день февраля, Date уезжает в март. Вторая строка показывает, что date-fns умеет и относительное время, но для показа хватает RelativeTimeFormat из примера выше.
| Библиотека | Версия | Вес или цена | Проверено |
|---|---|---|---|
temporal-polyfill | 1.0.5 | 20,1 КБ (глобальный Temporal) | 14.09.2026 |
date-fns | 4.4.0 | 5,6 КБ (format + addDays) | 14.09.2026 |
dayjs | 1.11.23 | 3,3 КБ (add + format) | 14.09.2026 |
Роли у трёх строк разные. temporal-polyfill — стандарт, который уйдёт сам, когда Temporal будет везде. date-fns — функции над обычным Date для кода, который пишется сейчас и не хочет ждать Safari. dayjs — запас с особой ролью: описание пакета в реестре называет его заменой Moment.js с тем же API, поэтому это путь для старого кода на moment, который стоит в разделе «Сошли с полки». Luxon здесь нет не из-за качества — причина в разделе «Не на полке и почему».
Пересмотреть, если Temporal появится в Safari: полифил уходит, а библиотека остаётся только ради старого кода.
Не нужна для формы и своих данных: хватит встроенной проверки и разбора. Платформа: встроенная проверка формы (constraint validation) — Widely с 11.06.2021. required, pattern, min и max пишутся прямо в разметке, а правило из той же статьи — браузерная проверка — про удобство, серверная — про доверие — не отменяет никакая библиотека.
Своя сборка — в этой же части. Правила, которых браузер знать не может, подключаются через setCustomValidity, а данные, восстановленные из хранилища, в проекте разбирались руками — проверкой каждого поля через typeof. В части III тот же разбор получит типы, а в статье «Данные извне — это unknown», в разделе «Когда данные не подошли», снова встанет выбор между своим разбором и библиотекой схем.
Стена — не сложность одной проверки, а их число. Когда полей десятки, а форма данных одна и та же в форме, в хранилище и на сервере, её хочется описать один раз — схемой — и получать из неё и проверку, и список того, что не сошлось. Это работа библиотеки схем. Для браузера на полке Valibot: каждая его функция — отдельный экспорт, и в сборку попадает только то, чем код пользуется. Для контракта, общего с сервером, — Zod. Обе реализуют Standard Schema — общий интерфейс, через который форма или сервер принимают схему любой из них; разбирается он на полке React и Next.
У Zod есть ловушка веса, и выглядит она как противоречие пройденному. В статье о статичности импортов форма импорта вес почти не меняла: import { debounce } from "lodash-es" и import * as _ с вызовом _.debounce() давали одинаковый бандл, а решало то, раскладывает ли библиотека код на отдельные экспорты. У Zod замер одной и той же схемы в трёх формах импорта расходится в разы — esbuild 0.28.2, zod 4.6.5, 14.09.2026:
| Строка импорта | Вес |
|---|---|
import { z } from "zod" | 90,5 КБ |
import * as z from "zod" | 24,2 КБ |
import * as z from "zod/mini" | 4,3 КБ |
Противоречия нет, есть устройство пакета. В zod 4.6.5 z — не функция, а один экспорт-объект со всей библиотекой внутри: файл index.js пакета делает import * as z from "./v4/classic/external.js" и отдаёт его наружу строкой export { z, z as default }. Именованный импорт этого объекта — тот самый «один огромный объект», про который статья об импортах говорила, что доказать ненужность остального трудно. esbuild и не доказывает: кладёт в сборку всё. А обращения через import * as z он разбирает по свойствам и оставляет использованное. Комментарий в том же index.js утверждает, что Rollup и webpack умеют выкидывать лишнее и из именованного z, — это мы не проверяли, поэтому говорим «в esbuild», а не «всегда». Вывод шире Zod: форма импорта решает, но как именно, зависит и от того, как библиотека экспортирует, и от того, чем собирают.
Вторая разница между библиотеками пригодится на полке React и Next: путь до поля с ошибкой они записывают по-разному. Обе схемы ниже отвечают через интерфейс Standard Schema — поле "~standard":
У zod путь — строка "title", у valibot — объект с полем key; для печати он свёрнут в {key:title}. Код, который принимает схему любой библиотеки, обязан понимать оба вида.
| Библиотека | Версия | Вес или цена | Проверено |
|---|---|---|---|
valibot | 1.5.0 | 1,1 КБ (object + parse) | 14.09.2026 |
zod | 4.6.5 | 24,2 КБ (import * as z); zod/mini — 4,3 КБ | 14.09.2026 |
На полке обе: Valibot — для браузера, Zod — когда та же схема живёт и на сервере. У обеих в реестре по одному сопровождающему — это пометка о риске, а не отказ.
Пересмотреть, если выйдет zod 5 или Standard Schema 2.
Не нужна, если хватает structuredClone, Object.groupBy и Set. У библиотек утилит платформа за последние годы забрала больше всего. structuredClone — Widely с 14.09.2024. Object.groupBy и Promise.withResolvers — Widely с 05.09.2026, за девять дней до снимка. Методы Set вроде union и intersection — Newly с 11.06.2024, Widely станут 11.12.2026. Итераторные хелперы — Newly с 31.03.2025, Math.sumPrecise — Newly с 10.04.2026.
Своя сборка: глубокая копия, ради которой раньше тащили lodash, и Set без дублей. Чего в платформе нет — debounce и throttle, отложенного и прореженного вызова. Написать их можно и самому, но за первыми десятью строками идут отмена, вызов в начале серии, вызов в конце серии, досрочный вызов — у debounce из es-toolkit это методы cancel и flush и опция edges. Ради такого и держат маленькую библиотеку.
Снимок сравнивает две библиотеки на одном и том же импорте — debounce и groupBy. groupBy в замере ради честного сравнения: в новом коде его заменяет Object.groupBy.
| Библиотека | Версия | Вес или цена | Проверено |
|---|---|---|---|
es-toolkit | 1.52.0 | 0,4 КБ (debounce + groupBy) | 14.09.2026 |
lodash-es | 4.18.1 | 7,1 КБ (debounce + groupBy) | 14.09.2026 |
Разница почти в 17 раз при одной и той же строке import { debounce, groupBy }, и дело здесь не в форме импорта, как у Zod, а в том, что внутри. groupBy из lodash принимает вместо функции ещё и имя свойства или объект-образец, и эта поддержка тянет за собой общий механизм сопоставления значений. Честная оговорка: lodash не мёртв — 4.18.1 вышла 01.04.2026, — он просто тяжелее. На полке es-toolkit; lodash-es — запас для проекта, где lodash уже есть.
Пересмотреть, если методы Set станут Widely, — по датам web-features это 11.12.2026: тогда пересечения и разности наборов пишутся без оговорки про старые браузеры.
Не нужна для одного-трёх простых графиков: polyline в SVG. Платформа: SVG и canvas, оба Widely давно. Своей сборки в учебнике нет: SVG мелькнул одной строкой — в разделе о createElement сказано, что для графики нужен createElementNS, — а графиков ни одна статья не строила. Поэтому своя сборка здесь.
Двенадцать месяцев выручки, линия и подписи оси. Ломаную линию в SVG рисует элемент polyline: в его атрибуте points через пробел записаны пары «x,y». Ось y в SVG смотрит вниз, поэтому чем больше выручка, тем меньше y. Метод getBBox() возвращает рамку, в которую уместилась нарисованная линия. Прогноз: какие числа выйдут в делениях оси?
Некруглые подписи оси на настоящий график никто не повесит: человек ждёт круглых делений, и это первая строка стены. Дальше подсказка с числом при наведении, доступность для программ чтения экрана, тысячи точек без тормозов, масштаб и легенда. Каждая строка — отдельная работа, и вместе они и есть библиотека графиков.
Снимок — три библиотеки для трёх размеров задачи: Chart.js рисует на canvas обычные графики, uPlot — большие временные ряды, ECharts — дашборды и заметно тяжелее.
| Библиотека | Версия | Вес или цена | Проверено |
|---|---|---|---|
chart.js | 4.5.1 | 48,2 КБ (линия); auto — 68,7 КБ | 14.09.2026 |
uplot | 1.6.32 | 22,5 КБ | 14.09.2026 |
echarts | 6.1.0 | 158,8 КБ (core + линия + canvas) | 14.09.2026 |
Две оговорки, найденные при проверке. У Chart.js за последний год один выпуск — 4.5.1 от 13.10.2025, и 13.10.2026 исполнится 12 месяцев без новой версии. Коммиты в репозиторий при этом идут, последний — 14.09.2026. Вопрос 1 он пока проходит и стоит на полке с этим условием. У uPlot выпуска нет с 14.03.2025, и по датам вопрос 1 он уже не проходит. Но репозиторий жив — за год больше 30 коммитов, последний 14.09.2026, — поэтому uPlot стоит на полке только с пометкой: законченная библиотека, проверено по репозиторию.
Пересмотреть, если к 14.10.2026 у Chart.js не выйдет новая версия: после года без выпуска его, как uPlot, проверяют по репозиторию.
Не нужна для наведения, появления и смены состояний: CSS и element.animate(). Платформа: Web Animations API — Widely с 16.03.2023; View Transitions в пределах одной страницы — Newly с 14.10.2025; анимации, привязанные к прокрутке, не Baseline — в Firefox их нет.
Своя сборка — в прологе, и там же названа стена. Переход кончается там, где движением нужно управлять из JavaScript, а ключевые кадры — там, где путь решается по ходу движения: пружина, которая помнит скорость жеста, исчезновение удалённого элемента, перестройка раскладки.
За этой стеной на полке Motion, и у него две весовые категории. motion/mini строит анимацию на том же element.animate() и весит как небольшой модуль; полный animate из motion тяжелее примерно в шесть раз. Исчезновение и раскладка в React у Motion свои, и их разбирает статья «Анимация в React» в части IV.
| Библиотека | Версия | Вес или цена | Проверено |
|---|---|---|---|
motion | 14.0.0 | 3,7 КБ (motion/mini); animate — 21,6 КБ | 05.10.2026 |
gsap | 3.15.0 | лицензия GSAP Standard, не MIT; вес не мерил | 14.09.2026 |
GSAP — запас, и главное о нём не вес, а лицензия. В поле license реестра у него не MIT, а ссылка на стандартную лицензию GSAP. По её тексту (сверен 14.09.2026, редакция страницы от 30.05.2025) порога выручки нет и коммерческое использование бесплатно, а запрещено встраивать GSAP в no-code конструкторы визуальных анимаций, которые конкурируют с Webflow. Сайта или приложения это условие обычно не касается, но прочитать его до установки — ровно тот случай, ради которого поле license и открывают.
Пересмотреть, если анимации по прокрутке появятся в Firefox.
Не нужна для сотен строк: table и методы массивов. Платформа: <table>, filter и toSorted, последний — Widely с 04.01.2026. Своя сборка — в проекте этой части: функция render собирает разметку из состояния через map и join, а щелчки ловит один обработчик на контейнере. Сортировка по столбцу — ещё один toSorted перед тем же render.
Стена здесь названа прямо — тысячи строк. Когда строк тысячи, дорогим становится сам DOM: узлов слишком много, чтобы пересобирать их все на каждую сортировку. Выход — виртуализация: рисовать только строки, которые видны, и подставлять остальные при прокрутке. Рядом копятся сортировка по нескольким столбцам, фильтры по каждому и группировка.
TanStack Table даёт модель строк без разметки — сортировку, фильтры, группировку, страницы, — а рисуете вы. Виртуализации в нём нет: среди 109 экспортов @tanstack/table-core 9.2.4 нет ни одного про virtual, её добавляют отдельно. И ловушка свежего мажора: у версии 9, вышедшей 04.08.2026, другой API. createTable и getCoreRowModel больше не экспортируются, вместо них constructTable, coreFeatures и функции вида createSortedRowModel, так что уроки и ответы по версии 8 с ней не совпадут.
| Библиотека | Версия | Вес или цена | Проверено |
|---|---|---|---|
@tanstack/table-core | 9.2.4 | 9,2 КБ (constructTable + сортировка) | 14.09.2026 |
ag-grid-community | 36.1.0 | MIT, часть функций в платной Enterprise; вес не мерил | 14.09.2026 |
AG Grid Community — запас, если нужен готовый компонент таблицы, а не модель: пакет под MIT, но часть функций продаётся в платной Enterprise.
Пересмотреть, если выйдет TanStack Table 10 или AG Grid сменит лицензию Community.
Не нужна для текста: textContent. Платформа: textContent работает всегда. Sanitizer API — метод setHTML, который вставляет HTML, вычищая опасное, — не Baseline: он есть в Chrome с 146 и Firefox с 148, а в Safari его нет. Trusted Types — режим, в котором innerHTML принимает не любую строку, а только значение, созданное вашей политикой очистки, — Newly с 24.02.2026.
Своя сборка — статья о чужих строках: onerror из innerHTML выполняется, а самодельные фильтры обходят лучше, чем пишут. Проверим это на самом частом самодельном фильтре — регулярке, которая вырезает <script>. Прогноз: выполнится ли чужой код?
<script>, и чужой код всё равно выполнился: картинка с несуществующим адресом получила ошибку загрузки, и браузер запустил её onerror.Атрибутов-обработчиков в HTML десятки, а способов записать тег так, чтобы регулярка его не узнала, ещё больше. Это стена: разбирать HTML должен разборщик HTML, а не регулярное выражение.
Там, где метод есть, разбор берёт на себя браузер. Та же строка через setHTML. Прогноз: что останется от комментария?
setHTML по умолчанию выбросил и <script>, и onerror, и саму картинку — строже, чем обычно ждут. Картинку можно вернуть своим списком разрешённых элементов и атрибутов, а onerror не вернуть никак: обработчики setHTML снимает при любом списке, в обоих движках.Вывод снят в Chrome 152, Firefox 155 дал то же самое; в Safari метода нет, и пример напечатает запасную строку.
Пока setHTML есть не везде, чужой HTML чистит DOMPurify. Тот же комментарий через него — страница с модулем, собранным esbuild, вывод из консоли вкладки:
DOMPurify по умолчанию мягче: картинку оставил, а onerror выбросил, у ссылки снял javascript:, у <b> — onclick. Вставленный результат чужой код не запустил.
| Библиотека | Версия | Вес или цена | Проверено |
|---|---|---|---|
dompurify | 3.4.15 | 10,9 КБ | 14.09.2026 |
Пересмотреть, если Sanitizer API появится в Safari: DOMPurify станет запасным путём.
Не нужна для открытия, фокуса и верхнего слоя: popover и dialog. Платформа: атрибут popover — Newly с 27.01.2025; <dialog> — Widely с 14.09.2024; команды кнопок command и commandfor — Newly с 12.12.2025. Что из этого пролог советовал писать, а что пока только знать, — в разделе «Что пока требует скрипта или слишком свежее». CSS anchor positioning, которая ставит подсказку прямо у кнопки, не Baseline: по web-features 3.38.0 целиком её пока не поддерживает ни один браузер, и пролог предупреждал не рассчитывать на неё.
Своя сборка — подсказка рядом с элементом из этой части: координаты из getBoundingClientRect, у нижнего края окна подсказка переворачивается наверх, у правого — сдвигается влево. Руками, но работает.
Стена — словами, без примера: рамка примера слишком мала, чтобы честно показать переворот. Своя подсказка знает только края окна. Поставьте кнопку внутрь списка с собственной прокруткой, а список — внутрь панели, которая тоже прокручивается, и подсказку придётся пересчитывать при прокрутке каждого из них и при изменении их размеров. В @floating-ui/dom 1.8.0 это отдельные экспорты: computePosition с flip и shift считает положение, getOverflowAncestors находит всех прокручиваемых предков, а autoUpdate пересчитывает положение, когда они прокручиваются или меняют размер.
| Библиотека | Версия | Вес или цена | Проверено |
|---|---|---|---|
@floating-ui/dom | 1.8.0 | 5,6 КБ (computePosition + flip + shift + offset) | 14.09.2026 |
Пересмотреть, если anchor positioning станет Baseline.
Не нужна для пары строк: хватит localStorage. Платформа: IndexedDB — Widely с 20.03.2024; navigator.storage с persist() и estimate() — Widely с 18.03.2026.
Своя сборка — хранилища, которые уже встречались. У localStorage около 5 МБ и синхронная запись, а таблица мест хранения из проекта части I отдаёт много данных, файлы и офлайн IndexedDB. И оговорка из главы про офлайн, которая касается любой обёртки: Safari удаляет данные сайта, в том числе IndexedDB, после семи дней пользования браузером без единого нажатия или касания на страницах сайта.
Стена — сам API. IndexedDB появилась в браузерах раньше промисов, и открытие базы, каждое чтение и каждая запись в ней — объект-запрос с событиями success и error, а схема базы меняется в обработчике upgradeneeded. Код на таких запросах быстро превращается в лестницу обработчиков. idb заворачивает их в промисы: await db.get("tasks", 1) вместо запроса с двумя обработчиками. Запускаемого примера здесь нет: у рамки примера уникальное происхождение, и indexedDB.open в ней бросает SecurityError.
| Библиотека | Версия | Вес или цена | Проверено |
|---|---|---|---|
idb | 8.0.3 | 1,3 КБ | 14.09.2026 |
С idb полка делает исключение. По датам вопрос 1 он не проходит: выпуска нет с 07.05.2025, коммитов за год нет. Законченную библиотеку от брошенной по датам не отличить, это решает человек — по issues, и ответ там честный, а не красивый: 57 открытых обсуждений, предложенные исправления ждут месяцами и годами. Для обёртки в 1,3 КБ над стабильным API это приемлемо: ломаться в ней почти нечему, а её код читается за вечер. Для большой библиотеки такой ответ был бы отказом.
Пересмотреть, если idb сломается в новом браузере и исправление не выйдет или сопровождающий объявит проект закрытым.
Эти пакеты ещё встречаются в старых проектах, курсах и ответах на форумах. У каждого — что случилось, номер вопроса из «Живой ли пакет», на котором он отсеялся, и что брать вместо. Вопросы, которые понадобятся здесь и в следующем разделе: 0 — умеет ли это платформа, 1 — жив ли пакет, 2 — растёт ли его доля в нише, 3 — цена, 4 — навязывает ли пакет устройство проекта.
| Пакет | Что случилось | Вопрос | Что вместо |
|---|---|---|---|
moment | загрузки за неделю — ×1,03 к той же неделе 2025 года; сопровождающие сами называют проект legacy в режиме поддержки; выпуск 2.31.0 от 15.09.2026 закрыл уязвимость, а до него новых версий не было с 27.12.2023 | 2 | Temporal с полифилом или date-fns для нового кода, dayjs для старого — раздел «Даты» |
gridjs | последний выпуск 6.2.0 — 03.03.2024; в репозитории за год 3 коммита, последний 29.01.2026, и выпуска после них нет | 1 | TanStack Table — раздел «Таблицы» |
request здесь не стоит: это HTTP-клиент для Node, в браузере он не жил. Его пометка deprecated разобрана в статье «Живой ли пакет».
Сошедший пакет не выкорчёвывают завтра. Но новый код на нём не пишут, а при следующей большой правке меняют.
Пакеты ниже живы: пометок deprecated нет, выпуски свежие. Исключение — Luxon: выпуск старше года, и живость решает дата, см. ниже. Отказ у каждого — не оценка качества, а факт, который можно проверить: у четырёх это вопрос статьи «Живой ли пакет», у Luxon — место на полке, которое уже занято.
| Пакет | Что проверено | Почему не на полке |
|---|---|---|
| Tailwind CSS | tailwindcss 4.3.3 от 16.07.2026 | вопрос 4: стили переезжают в разметку классами. Учебник пишет стили CSS-модулями — это решение про устройство проекта, а не оценка качества |
| ApexCharts | apexcharts 7.3.0 от 12.09.2026: файлы LICENSE и src/modules/license/LicenseEnforcer.js в самом пакете | вопрос 3: бесплатная Community License — только организациям с годовой выручкой меньше 2 млн долларов, остальным нужна платная; премиум-возможности без ключа рисуют на графике водяной знак |
| axios | axios 1.20.0 от 26.08.2026 | вопрос 0: fetch встроен. Две вредоносные версии, попавшие в реестр 31.03.2026 и снятые, — не причина отказа, а урок о свежих выпусках из статьи «Живой ли пакет» |
| jQuery | jquery 4.0.0 от 18.01.2026 | вопрос 0: querySelector, classList и fetch закрыли его задачи |
| Luxon | luxon 3.7.2 от 05.09.2025 | роль — абзац ниже; с 05.10.2026 и живость не проходит по дате |
Строки ApexCharts и GSAP с полки «Анимация» стоит сравнить. Обе лицензии не MIT, но условия разные: порог выручки ApexCharts касается работодателя читателя, а у GSAP порога нет вовсе — его условие касается конкурирующих no-code конструкторов.
Luxon — самая спорная строка, поэтому подробно. По живости он на грани: на дату снимка выпуску 3.7.2 год и девять дней — функция health() из статьи «Живой ли пакет» считает полные месяцы, насчитывает ровно 12 и с порогом «больше 12 месяцев» пропускает; с 05.10.2026 месяцев станет 13, и тот же порог его завернёт: выпуска старше года у пакета уже нет. Репозиторий жив (23 коммита за год), загрузки выросли ×1,70 — но первый вопрос с этой даты решается по дате, как у uPlot. Причина отказа — роль. Luxon берут ради неизменяемых дат с часовыми поясами, а это место на полке занимает Temporal с temporal-polyfill: полифил удаляется одной строкой, когда Temporal будет везде, а объекты DateTime из Luxon остались бы в каждом вызове. Функции над Date для нынешнего кода — date-fns, путь со старого moment — dayjs.
Отказ по цене или по устройству проекта не запрещает пакет в чужом проекте. Он говорит, чем придётся платить позже, и знать это нужно до установки.
- Полка начинается с платформы: для каждой задачи сначала то, что браузер умеет сам, и его статус Baseline; Newly — всегда с запасным путём, «пока не умеет» — всегда с условием пересмотра.
- Спрашивают возможность, а не имя браузера: проба отвечает за этот браузер, статус — за все. Полифил уходит сам, когда стандарт догонит, а вызовы библиотеки остаются в коде.
- Intl показывает дату в поясе, «вчера», рубли и списки без библиотеки. Стена — арифметика дат, и за ней Temporal с полифилом или date-fns.
- Форма импорта решает вес, но не одинаково для всех: у zod 4.6.5
import { z }в esbuild тяжелееimport * as zпочти вчетверо, потому чтоz— один объект со всей библиотекой. - Самодельный фильтр пропускает
onerror;setHTMLв Chrome и Firefox выбрасывает и обработчики, и картинки, а в Safari его нет — там работает DOMPurify. SVG-график на двенадцати точках строится без библиотеки, но круглые деления оси — уже стена. - Каждая таблица полки — снимок на 14.09.2026 с весом типичного импорта и условием «Пересмотреть, если…»; сошедшие и отвергнутые пакеты названы с датой и вопросом, на котором они отсеялись.
1/4В браузере читателя проба CSS.supports("anchor-name", "--tip") вернула true. Что это значит для подсказок на anchor positioning?
2/4Какая строка импорта zod 4.6.5 даёт в сборке esbuild самый тяжёлый результат?
3/4Temporal появился во всех основных браузерах. Что будет с кодом на temporal-polyfill и с кодом на date-fns?
4/4Самодельный фильтр вырезал из комментария теги <script> и вставил остаток через innerHTML. В остатке — картинка с несуществующим адресом и атрибутом onerror. Что произойдёт?
Круглые деления оси
В примере с графиком деления оси вышли некруглыми. Здесь та же функция отдельно, на четырёх максимумах. Перепишите ticks так, чтобы шаг был круглым — 1, 2 или 5, умноженные на степень десяти, — промежутков между делениями было не больше parts, а последнее деление было не меньше максимума. Сначала запустите как есть и найдите в выводе вторую беду, кроме некруглых чисел.
Шаг берут из «круглого ряда»: сырой шаг max / parts приводят к степени десяти и выбирают первое из 1, 2, 5 и 10, которое не меньше него:
Вывод: 0 · 20 · 40 · 60, 0 · 2 · 4 · 6 · 8, 0 · 500 · 1000 · 1500 и 0 · 0.2 · 0.4 · 0.6 · 0.8. Вторая беда исходного вывода — 0.6000000000000001: 0.8 * 3 в двоичной арифметике даёт не 2,4, а 2.4000000000000004. toPrecision(12) отрезает этот хвост.
Круглая ось поменяла и сам график: верх теперь 60, а не 52, и линию придётся масштабировать под новый верх. А на максимуме 0 функция вернёт пустой список делений, и об отрицательных значениях она не знает вовсе. Каждая такая мелочь — строка стены, и в библиотеке графиков они уже решены.
Какая строка полки
Для каждой задачи назовите раздел полки и решите: хватит платформы или нужна библиотека — и какая.
| № | Задача |
|---|---|
| 1 | показать дату заказа в часовом поясе покупателя |
| 2 | напомнить об оплате через месяц после 31 января |
| 3 | вывести комментарий пользователя с жирным шрифтом и ссылками |
| 4 | таблица на 50 000 строк с сортировкой по нескольким столбцам |
| 5 | подсказка у кнопки внутри списка с собственной прокруткой |
1. «Даты», платформы хватает: Intl.DateTimeFormat с опцией timeZone — ровно как Владивосток в примере раздела.
2. «Даты», но это уже арифметика: Date уедет в март. Temporal с полифилом или addMonths из date-fns дадут 28 февраля.
3. «Чужой HTML». textContent не подходит: разметку нужно сохранить. Где есть setHTML, он справится сам: жирный шрифт и обычную ссылку он по умолчанию оставляет, это проверено в Chrome 152 и Firefox 155. В Safari метода нет, и запасной путь — DOMPurify.
4. «Таблицы», и одной библиотеки мало: TanStack Table даст модель строк с сортировкой по нескольким столбцам, а виртуализацию — рисовать только видимые строки — добавляют отдельно.
5. «Поповеры и подсказки»: своя подсказка знает только края окна, а здесь нужен пересчёт при прокрутке списка — computePosition и autoUpdate из @floating-ui/dom.
Картинку оставить, обработчик — нет
Пример вставляет комментарий через setHTML, и настройки по умолчанию выбрасывают фотографию вместе с обработчиком. Сделайте так, чтобы картинка с src и alt осталась, а чужой код по-прежнему не выполнился. Подсказка: второй аргумент setHTML принимает объект с полем sanitizer, а в нём — списки elements и attributes. В Safari метода нет, и там пример напечатает запасную строку.
Разрешённые элементы и атрибуты перечисляют явно:
Теперь печатается <p>Смотрите фото:</p><img src="x" alt="фото">, а через 300 мс — hacked: undefined. Картинка с адресом x по-прежнему не загружается и получает ошибку, но обработчика у неё больше нет.
Допишите "onerror" в attributes — вывод не изменится: обработчики событий setHTML снимает при любом списке, так в Chrome 152 и Firefox 155. Если обработчик действительно нужен, его вешают своим кодом через addEventListener, а не берут из чужой строки.