Почему JSON.parse() может быть быстрее объектного литерала
Казалось бы,
Причин несколько. Во-первых, грамматика JSON тривиальна по сравнению с JS — у движка для неё отдельный, более простой и быстрый парсер. Объектный литерал — это полноценный JS-код, который проходит весь путь: токенизация → AST → байткод. Так же, как описано в блоге V8, большие объектные литералы могут парситься дважды — сначала при preparsing, потом при lazy-parsing. Строка внутри JSON.parse этой проблемы лишена.
В реальном кейсе с SSR-приложением на Redux такая замена дала улучшение Lighthouse-скора с 87 до 95 и снижение TTI на 0.7 секунды. Оптимизация актуальна и в 2026 году — фундаментальные причины никуда не делись, а V8 продолжает активно инвестировать в производительность JSON (например, недавний двукратный прирост JSON.stringify в V8 v13.8).
Казалось бы,
const config = {a: 1, b: 2, c: 3} — это самый прямой способ создать объект. Но если объект достаточно большой (от ~10 KB), JSON.parse('{"a":1,"b":2,"c":3}') окажется быстрее. На бенчмарке от GoogleChromeLabs на файле в 7 МБ JSON.parse оказался в 1.7× быстрее объектного литерала в V8, в Safari разница доходила до 2×.Причин несколько. Во-первых, грамматика JSON тривиальна по сравнению с JS — у движка для неё отдельный, более простой и быстрый парсер. Объектный литерал — это полноценный JS-код, который проходит весь путь: токенизация → AST → байткод. Так же, как описано в блоге V8, большие объектные литералы могут парситься дважды — сначала при preparsing, потом при lazy-parsing. Строка внутри JSON.parse этой проблемы лишена.
В реальном кейсе с SSR-приложением на Redux такая замена дала улучшение Lighthouse-скора с 87 до 95 и снижение TTI на 0.7 секунды. Оптимизация актуальна и в 2026 году — фундаментальные причины никуда не делись, а V8 продолжает активно инвестировать в производительность JSON (например, недавний двукратный прирост JSON.stringify в V8 v13.8).
🤯30🔥9❤3👍2💅1
Что такое back/forward cache (bfcache)
Когда пользователь нажимает «назад» или «вперёд», браузер может не загружать страницу заново, а достать из памяти полный снимок: DOM, JS-кучу, состояние скролла — всё. Страница замораживается при уходе и размораживается при возврате. Переход ощущается мгновенно, потому что это не навигация в привычном смысле — это restore.
Браузер делает эту оптимизацию автоматически, но страница должна соответствовать определённым условиям. Она не попадёт в bfcache, если:
- есть listener на
- открыт WebSocket
- на документе стоит
- есть незавершённая `IndexedDB`транзакция
- и другие причины
Полный список можно посмотреть на MDN.
Диагностировать всё это можно прямо в DevTools: вкладка Application → Back/forward cache. Там можно протестировать, попадает ли страница в bfcache, и если нет — увидеть конкретный список блокеров.
Для продакшена есть программный способ — PerformanceNavigationTiming.notRestoredReasons. Через него можно собирать данные об использовании bfcache в RUM-метриках и понимать, что ломает кэш у реальных пользователей.
По итогу bfcache — один из самых «дешёвых» способов ускорить воспринимаемую производительность. Ничего не нужно дополнительно оптимизировать — достаточно не ломать нативное поведение браузера.
Когда пользователь нажимает «назад» или «вперёд», браузер может не загружать страницу заново, а достать из памяти полный снимок: DOM, JS-кучу, состояние скролла — всё. Страница замораживается при уходе и размораживается при возврате. Переход ощущается мгновенно, потому что это не навигация в привычном смысле — это restore.
Браузер делает эту оптимизацию автоматически, но страница должна соответствовать определённым условиям. Она не попадёт в bfcache, если:
- есть listener на
unload- открыт WebSocket
- на документе стоит
Cache-Control: no-store- есть незавершённая `IndexedDB`транзакция
- и другие причины
Полный список можно посмотреть на MDN.
Диагностировать всё это можно прямо в DevTools: вкладка Application → Back/forward cache. Там можно протестировать, попадает ли страница в bfcache, и если нет — увидеть конкретный список блокеров.
Для продакшена есть программный способ — PerformanceNavigationTiming.notRestoredReasons. Через него можно собирать данные об использовании bfcache в RUM-метриках и понимать, что ломает кэш у реальных пользователей.
По итогу bfcache — один из самых «дешёвых» способов ускорить воспринимаемую производительность. Ничего не нужно дополнительно оптимизировать — достаточно не ломать нативное поведение браузера.
🔥38❤4👍4👏1💅1
Продолжая тему с небольшими оптимизациями в браузере — сегодня поговорим про заголовок
Полностью он используется так:
Что здесь происходит:
- 0–600 сек — ресурс свежий, отдаётся из кэша
- 600–630 сек — ресурс устарел, но браузер всё равно отдаёт его мгновенно, а в фоне идёт за новой версией
- после 630 сек — кэш полностью стух, пользователь ждёт
Ключевой момент: пользователь, попавший в stale-окно, видит старую версию. Фоновый запрос незаметно обновляет кэш, и уже в следующий раз посетитель получит свежие данные.
Где его используем, а где нет?
- Нехешированная статика (`/logo.png`, `/fonts/custom.woff2`) — используем, потому что URL не меняется при обновлении файла.
- API-ответы и HTML, которые меняются нечасто (каталог, лендинг, результаты поиска) — тоже используем, это даёт мгновенный ответ, а данные отстают максимум на пару минут.
- Хешированная статика (`main.a3f8c2.js`) — не имеет смысла использовать
- Критичные данные (баланс, цена, статус заказа) — тут уже нужен
И да, паттерн SWR знаком многим по React-библиотекам (swr, React Query), но лично я раньше не задумывалась о том, что это буквально тот же принцип, только взятый из HTTP.
stale-while-revalidate.Полностью он используется так:
Cache-Control: max-age=600, stale-while-revalidate=30Что здесь происходит:
- 0–600 сек — ресурс свежий, отдаётся из кэша
- 600–630 сек — ресурс устарел, но браузер всё равно отдаёт его мгновенно, а в фоне идёт за новой версией
- после 630 сек — кэш полностью стух, пользователь ждёт
Ключевой момент: пользователь, попавший в stale-окно, видит старую версию. Фоновый запрос незаметно обновляет кэш, и уже в следующий раз посетитель получит свежие данные.
Где его используем, а где нет?
- Нехешированная статика (`/logo.png`, `/fonts/custom.woff2`) — используем, потому что URL не меняется при обновлении файла.
- API-ответы и HTML, которые меняются нечасто (каталог, лендинг, результаты поиска) — тоже используем, это даёт мгновенный ответ, а данные отстают максимум на пару минут.
- Хешированная статика (`main.a3f8c2.js`) — не имеет смысла использовать
stale-while-revalidate, тут правильнее будет immutable, потому что файл по этому URL с хэшом не изменится никогда.- Критичные данные (баланс, цена, статус заказа) — тут уже нужен
no-cache.И да, паттерн SWR знаком многим по React-библиотекам (swr, React Query), но лично я раньше не задумывалась о том, что это буквально тот же принцип, только взятый из HTTP.
❤22👀1💅1
Недавно на рабочем проекте я обновляла Next.js с 12-й на 16-ю версию. Далось непросто, так как были кастомный сервер на NestJS и связка через пакет nest-next, который последний раз обновлялся три года назад. Больно и неприкольно, но мы справились 💪
Этот опыт вдохновил меня наконец заняться тем, что я так долго откладывала — сходить в отпуск. Ну и написать цикл статей про Next.js)
Так что впереди нас ждёт трёхнедельный перерыв на канале, а после него — новый цикл, не переключайтесь!
Этот опыт вдохновил меня наконец заняться тем, что я так долго откладывала — сходить в отпуск. Ну и написать цикл статей про Next.js)
Так что впереди нас ждёт трёхнедельный перерыв на канале, а после него — новый цикл, не переключайтесь!
1❤50🔥16👍6🙏1💯1💅1
А вот и обещанный новый цикл про Next.js под капотом.
И в первой части разберём, из каких слоёв состоит Next.js как система, как они связаны друг с другом и почему архитектура стала именно такой.
Next.js изнутри. Часть 1. Архитектура Next.js.
И в первой части разберём, из каких слоёв состоит Next.js как система, как они связаны друг с другом и почему архитектура стала именно такой.
Next.js изнутри. Часть 1. Архитектура Next.js.
1❤32🔥8💯2😐1💅1🤪1💊1
Продолжаем погружаться в Next.js. Сегодня разбираем Pages Router: что создаёт
Next.js изнутри. Часть 2. Как работает Pages Router.
next build, как сервер рендерит HTML при первом заходе и что происходит при клиентской навигации.Next.js изнутри. Часть 2. Как работает Pages Router.
2❤18👍4💅4
На очереди App Router, и материал получается такой плотный, что я решила разбить его на две части. В первой посмотрим на ключевые концепции и разберём момент первого рендеринга, без клиентской навигации и server actions.
Next.js изнутри. Часть 3. App Router: от запроса до гидратации.
Next.js изнутри. Часть 3. App Router: от запроса до гидратации.
1🕊9❤5💅5👍2🔥2
В продолжении разговора про App Router обсудим prefetch, Server Actions, клиентскую навигацию и даже немного затронем тему безопасности.
Next.js изнутри. Часть 4. App Router: навигация, кеш и мутации.
Next.js изнутри. Часть 4. App Router: навигация, кеш и мутации.
1❤7🤣3💅3👍2✍1
Не останавливаем своё погружение в Next.js! Сегодня поговорим про серверный слой и то, как вообще запрос доходит до рендера.
Next.js изнутри. Часть 5. Серверный слой.
Next.js изнутри. Часть 5. Серверный слой.
1👍12🔥3
А вот и моя самая любимая (или нет)) вещь в приложениях на Next.js — кастомный сервер! Посмотрим, что это такое, как он настраивается, и я даже поделюсь историей из своего рабочего проекта.
Next.js изнутри. Часть 6. Кастомный сервер.
Next.js изнутри. Часть 6. Кастомный сервер.
1🙏6❤4💅2
Потихоньку подходим к завершению цикла про Next.js (осталось немного!), и теперь на очереди сборка: Webpack, Turbopack, SWC и не только — что это такое, как оно всё работает и с чем его едят.
Next.js изнутри. Часть 7. Сборка.
Next.js изнутри. Часть 7. Сборка.
1❤17💅2👨💻1
Отдельно поговорим про dev-mode в Next.js и особенности его работы.
Next.js изнутри. Часть 8. Dev-mode.
Next.js изнутри. Часть 8. Dev-mode.
1❤🔥9💅1
На прошлой неделе я читала студентам лекцию про настройку инфраструктуры и рассказывала в том числе про кэширование слоёв при сборке Docker-образа.
Как всегда, стало интересно, как это работает под капотом?
Сборкой занимается BuildKit — компонент Docker, который исполняет Dockerfile. Он преобразует инструкции в граф операций и для каждой вычисляет ключ кэша. Ключи образуют цепочку: ключ текущей операции считается из её описания и ключа предыдущей. Если это
Если подходящий ключ уже есть в кэше, операция не выполняется, а BuildKit использует сохранённый результат. Если ключ изменился, операция выполняется заново. Следующие операции тоже получают другие ключи, поскольку зависят уже от нового результата.
Сам результат хранится как снимок файловой системы после операции. Локально снимки и служебные записи находятся во внутреннем хранилище конкретного builder-а: например, в Docker Desktop это хранилище внутри его Linux VM.
В CI всё зависит от runner-а. На постоянном runner-е локальный кэш может пережить сборку, а на одноразовом исчезнет вместе с машиной. Поэтому в CI кэш обычно явно экспортируют в registry или хранилище CI через
Именно поэтому в Dockerfile для Node.js сначала копируют
Ну а последняя часть цикла про Next.js будет на следующей неделе)
Как всегда, стало интересно, как это работает под капотом?
Сборкой занимается BuildKit — компонент Docker, который исполняет Dockerfile. Он преобразует инструкции в граф операций и для каждой вычисляет ключ кэша. Ключи образуют цепочку: ключ текущей операции считается из её описания и ключа предыдущей. Если это
COPY, к ним добавляется контрольная сумма копируемых файлов. Например, изменение базового образа меняет ключ FROM, вслед за ним — ключ следующего RUN и так далее.Если подходящий ключ уже есть в кэше, операция не выполняется, а BuildKit использует сохранённый результат. Если ключ изменился, операция выполняется заново. Следующие операции тоже получают другие ключи, поскольку зависят уже от нового результата.
Сам результат хранится как снимок файловой системы после операции. Локально снимки и служебные записи находятся во внутреннем хранилище конкретного builder-а: например, в Docker Desktop это хранилище внутри его Linux VM.
В CI всё зависит от runner-а. На постоянном runner-е локальный кэш может пережить сборку, а на одноразовом исчезнет вместе с машиной. Поэтому в CI кэш обычно явно экспортируют в registry или хранилище CI через
--cache-to, а перед следующей сборкой импортируют через --cache-from.Именно поэтому в Dockerfile для Node.js сначала копируют
package.json и lock-файл, затем выполняют npm ci и только после этого копируют исходники. Изменение кода в таком случае не инвалидирует слой с зависимостями. То есть порядок инструкций в Dockerfile определяет не только результат сборки, но и то, какую её часть придётся повторить при следующем запуске.Ну а последняя часть цикла про Next.js будет на следующей неделе)
👍25💅3❤2
Заключительная часть цикла про Next.js наконец-то здесь! Обобщим всё, о чём говорили до этого, и посмотрим на оптимизации.
Next.js изнутри. Часть 9. Способы оптимизации.
Next.js изнутри. Часть 9. Способы оптимизации.
1❤13💅1