Начинаем с чистой головой
Мой вывод - если нужно гнаться за sео, то NextJS (React) не выбор. Мой предел - 90-93 perfomance по оценке lighthouse. Предложение - NuxtJS (Vue.js).
И вот почему я считаю что это не случайностью:
Здесь стоит сделать оговорку о том насколько разные имена здесь и то, что предположительно первым всё равно на оптимизацию, но replit и сам vercel думаю с головой писали их сайты.
Главное что нужно в sео это оптимизация event loop, который я не смог получить на NextJS. Возможно для этого нужно переписать всё приложение, но как-то руки не дошли :) Пока дропаются фреймы, отличного перфоманса не будет.
Всё же, не всем нужно 100, а на NextJS писать легко и быстро, что также нужно продукту, поэтому ниже к моим наработкам.
Как тестить
Pro tip: если кажется что компонент неоптимизирован, то удалите его и сделайте оценку без него, возможно дело не в нем.
NPM пакет для локального тестирования - lighthouse. Намного стабильнее оценка чем в браузере. Получаете здесь стабильно 100 и вы топ! (буду ждать советов :) )
-
В браузере проверять в режиме Incognito. Выключает часть сбора всякой инфы и хоть на сайт напрямую это не влияет, но влияет на машину, проводящей оценки производительности.
Если остались включенные расширения с доступом в инкогнито - отключить. Влияют напрямую на загрузку сайта - могут что-то ренденрить, что-то грузить и тд. Адблокеры и гугл переводчики яркий пример.
Хром, даже делая обновление с очисткой кэша, будет что-то кэшировать и всё будет казаться куда оптимизированней. Я остановился в Firefox.
Делая оценку в браузере, после ребилда, обязательно делать новую оценку в новой вкладке, иначе часть будет закешированна.
Шрифты
Самый главный пункт.
Проверяем сколько шрифтов используется и уточняем у дизайнеров нужны ли они. Либо старо, либо уже неактуально, либо легко заменяется, но убирая лишние шрифты вы значительно ускоряете загрузку и упрощаете фикс CLS.
Уменьшаем количество глифов (символов) во всех оставшихся шрифтах с помощью сабсетов шрифтов. Для редактирования шрифтов мне очень был удобен fontsquirrel - делаем сабсеты нужных нам языков (для любого сайта только на английском спокойно хватает latin) и жирностей.
-
Google Fonts не доверяем и проверяем. В css файлах уже есть ссылки на семейство с разными начертаниями и разными сабсетами (
/* latin */
и/* latin-ext */
), но очень часто эти ссылки будут одинаковыми, где шрифты "всё в одном". Это не хак и весит он соответственно больше. Такое надо избегать и собирать сабсеты самим.Быстро посмотреть сколько глифов в шрифте можно, например в консоле. Через код элемента выбираем Elements → справа, где пишутся стили, переключаем вкладку Styles на Computed → снизу написано кол-во глифов в шрифте.
Шрифты подключать через <link rel="preload"/>. Это скажется значительно меньше на TBT чем на CLS.
Шиза-чек: Убрать любые шрифты из CSS в формате base64
(если с этим конечно можно выжить).CSS файлы вырастают до гигантских размеров и начинают грузиться несколько секунд.
Скрипты
-
Для NextJS с 11-ой версии - использовать только next/script (eslint будет ругаться с использованием расширения
plugin:@next/next/recommended
). В доке достаточно написано как использовать и какую стратегию выбирать. Вкратце:Указываем всегда разный id
Стратегии:
beforeInteractive
- менеджеры сессий, проверки юзера на бота и тд.afterInteractive
- менеджеры тэгов и аналитика.lazyOnload
- чат плагины и другие медиа виджеты (убедиться что используется не в _document иначе их не будет на сайте, можно добавить в _app)
Но этот пункт с одним замечанием - статья писалась по опыту оптимизации на NextJS 11.1.2 и тогда с next/script были проблемы. Сравнивая First Load JS при билде, используя next/script вместо обычного </script> First Load выростал на несколько кб.
-
Для NextJS до 11-ой версии - любые 3rd-party скрипты подключаем с атрибутом defer вместо async
Активно использовать next/dynamic, но только для элементов появляющися по взаимодействую с юзером - бургер меню, дропдауны, попапы и тд. С настройкой
{ ssr: false }
результат выходил лучше всегда.
Images
Здесь хотелось бы упоминуть next/image но не буду. На момент написания статьи <Image> не принимал классов и поэтому стилизовать их невозможно было. Обход через обертки очень плох, поскольку это создает 6 DOM нод (4 div + 1 img + 1 noscript) вместо одной, что не сильно но ухудшает рендер и может встать боком, так как lighthouse требует меньше 250 нод на странице.
Любые картинки, которые не влезают в экран при открытие, использовать с атрибутом
loading="lazy"
. Те что влезают обязательно использовать с width и height. Остальным картинкам они не обязательны, но желательны, так как иногда CLS может зацепить и их.-
Если используется много иконок то использовать image sprites - иконки закидывается в одну большую фотографию и используется через background-image и background-position.
Часто возникающий вопрос - что делать если динамичный размер иконки. Без JS проблему не решить:
import sprite from 'images/sprite.svg' const SpriteImage = ({ spriteCoors, defaultHeight, defaultWidth, height, width, }) => { const widthRatio = width / defaultWidth const heightRatio = height / defaultHeight const bgWidth = 216 * widthRatio const bgHeight = 193 * heightRatio const left = -spriteCoors.x * width const top = -spriteCoors.y * height return <div style={{ position: 'relative', width: `${width}px`, height: `${height}px`, background: `url(${sprite}) ${left}px ${top}px / ${bgWidth}px ${bgHeight}px no-repeat`, }} /> }
Все SVG прогонять через SVGOMG. Изредка может не работать и ломать картинку, но на большом количестве иконок сказывается положительно, уменьшая большую часть загрузки.
-
В какой-то момент нужны будут .webp, поэтому, на момент написания статьи, обязательно использование next-images
const withImages = require('next-images') module.exports = withImages({ // ... дальше обычный next.config.js })
Либы и пакеты
Обязательно проверить каждую либу - возможно она весит 30кб, используете то, что сможете переписать за 5 секунд (как у меня было со Swiper.js)
Оценка веса - bundlephobia. Возможно здесь у меня жиза, но битва за каждый кб. При 10кб уже стоит задуматься. В зависимости от пакета, 1кб может выиграть 1 пункт в Perfomance, а 30кб могут выиграть 20 пунктов.
Обязательно проверьте правильно ли вы используете известные библиотеки. На гитхабе next.js есть куча примеров включая популярные mobx и redux.
Общая оптимизация
Стоит напомнить про requestAnimationFrame. Если каждый фрейм что-то отрисовывается или делается проверка для рендера, то лучше использовать этот его и event loop будет чище.
Обработчики ивентов scroll, resize, touch должны работать через debounce или другой аналог. Никому точно не нужны миллион проверок и функций при каждом пикселе скрола.
Задуматься о апи запросах. Возможно у вас напрашивается batch запрос вместо тысячи единичных запросов.
Еще про апи - использовать шифрование и сжатие. Для koa это koa-compress. Для Nest.js используется compression.
-
Во всех useEffect возвращать функцию, которая будет очищать таймауты, удалять eventListeners и запрещать изменять стейт в асинхронных функциях. Это просто может выкинуть ошибку, но также может вызывать лишние рендеры
useEffect(() => { let isMounted = true async f () => { const data = await axios() if (isMounted) { // фиксит ошибку попытки изменить стейт unmounted компонента setState(data) } } window.addEventListener('scroll', () => {}) // помним про пункт 2 return () => { isMounted = false clearTimeout(timeout) window.removeEventListener('scroll', () => {}) } }, [])
Везде используемые next/link всегда используем с prefetch={false}. Не будет грузить вторых и третьих страниц.
То, что осталось за кадром
Web Workers. Вещь хорошая, если много логики. Не успел попробовать как сильно спасает и когда логики становится "много".
-
NextJS позволяет создать кастомный next/document, где можно теоритечески управлять загрузкой скриптов
Найденные скрипты не отличались перфомансом. Например этот.
Под копотом используется чистый нативный реакт, который оптимизированней с создание элементов, что достаточно влияет на перфоманс.
Какой-то очень оптимизированный скрипт загрузки стилей. Не успел попробовать.
Комментарии (6)
borovinskiy
20.02.2022 23:15+2По поводу производительности, было дело, наобещал заказчику, что будет 98 баллов, открываем, измеряем и тут оказывается, что оно в районе 73 и в желтой зоне.
Тут-то я и узнал, что производительность не нормализуется и измеряется в браузере на конкретном железе и если у тебя 98 баллов, это не значит, что у всех 98 баллов.Пример сайта цифровой библиотеки на Nuxt.js 2 на двух разных ПК.
Соответственно, надо не только мерить Lighthouse, но и смотреть на измеренные показатели по пользователям конкретного сервиса.
JamesJGoodwin
20.02.2022 23:48+1Мой вывод - если нужно гнаться за sео, то NextJS (React) не выбор
И вот почему я считаю что это не случайностью
То есть Вы по одному единственному параметру решили судить о применимости фреймворков в контексте оптимизации производительности для SEO? Какое-то более глубокое сравнение пытались делать? Например, искать корреляцию между количеством скриптов и DOM нод на страницах с использованием разных фреймворков?
В какой-то момент нужны будут .webp, поэтому, на момент написания статьи, обязательно использование next-images
Зачем? Достаточно перед загрузкой страницы проверить поддержку avif и webp в браузере через base64 fetch и передать расширение изображения в пропах. Это к Вашему высказыванию:
битва за каждый кб. При 10кб уже стоит задуматься
Еще про апи - использовать шифрование и сжатие. Для koa это koa-compress. Для Nest.js используется compression.
На этом моменте я совершенно запутался. Какое отношение это имеет к SEO и уж тем более какое отношение статья о NextJS имеет к NestJS? В ссылке на документацию к Nest которой вы поделились чёрным по белому написано:
For high-traffic websites in production, it is strongly recommended to offload compression from the application server - typically in a reverse proxy (e.g., Nginx)
С учётом того, что свой бэкенд с вероятностью 90% Вы будете хостить на PaaS, у которого есть свой собственный load balancer, он же reverse proxy, - то о сжатии будет беспокоиться именно он и в таком случае сжатие с однопоточного JavaScript движка лучше вообще снять, иначе можете сделать только хуже.
В конце-концов, готов оспорить самый главный тезис, озвученный Вами в этой статье. NextJS - это один из лучших, если не лучший инструмент для разработки UI с упором на оптимизацию. В подтверждение своих слов демонстрирую Вам скриншот аудита одного из наших NextJS проектов, где на мобильном устройстве оценка достигает 75-77/100 (https://imgur.com/a/20kkHTp), а на десктопе и вовсе 100/100 вышло (https://imgur.com/a/EN7TxOa). Аудит проводили на одной из самых "тяжёлых" страниц с множеством интерактивных компонентов, которая и близко не готова к продакшену - её только пару дней назад закончили "натягивать" с верстки на фреймворк, и ни о какой оптимизации тут и речи не идёт. Тем не менее. Также советую не тратить своё время на попытки добиться 90+ оценки для мобильных устройств, если Вы поставили себе цель гнаться за этим. В этом нет никакого смысла и добиться этого крайне трудно на сайте с интерактивными элементами. В какой-то момент Вы просто заметите, что тратите время на различные "хаки" и анти-паттерн приёмы, которые в дальнейшем будет тяжело поддерживать. Оно того не стоит.
В заключение предположу, что Вы просто недостаточно хорошо разобрались в теме оптимизации производительности в контексте SEO и решили, что фреймворк плохой и нежели тратить время на детальное изучение решили предложить не использовать его вообще.
andreysam
22.02.2022 11:09Можно попробовать использовать preact вместо react. Не знаю как с SEO, но производительность страниц значительно увеличивается, а размер бандла уменьшается. Жаль не все реакт библиотеки переваривают preact/compat
Vordgi
22.02.2022 22:36Для статей используйте, пожалуйста, универсальный инструмент, который не зависит от вкусов вашей машинки - https://pagespeed.web.dev/
Сравнил vuestorefront и vercel - в среднем в обоих случаях 72 балла на мобилке, на десктопе у vercel 95 баллов.
copilot.github.com - ужасный костыль (при всём уважении), грузящий и на десктопе и в мобильной версии картинки png в оригинальном качестве и выполняющий кучу логику на клиенте. В общем-то как и все остальные примеры, по содержимому можно устраивать конкурс на худшую реализацию.
---
Вообще, реальная проблема next - то, что они модерируют примеры не по качеству, а по имени. Увы. (если кому интересно, то речь о странице https://nextjs.org/showcase)
---
К счастью, на конфе с последнего релиза показали две действительно классные (в плане производительности) реализации - Pagespeed insights monogram.io - под 100 баллов на мобильных устройствах и macstadium.com - сейчас ~80, но раньше было также быстро (и по графику реальных пользователей видно, что 99% зеленые).
Vordgi
22.02.2022 22:47Я конечно понимаю, что web vitals с недавних пор участвуют при приоретизации сайтов в поисковой выдаче, но всё же это только в гугле. Когда я захожу на статью про SEO оптимизацию я ожидаю немного почитать про SEO. Здесь ни о чём кроме производительности речь не идёт.
Ожидалось про семантику, про то, как метатеги добавляются - где их можно добавить, как они потом мерджатся (при добавлении из разных мест); как работает Link от некста и зачем им своя реализация и почему везде нужно использовать её; почему они рекомендует пре-рендеринг, а не какой-нибудь серверный рендеринг и зачем сделали инкрементальный рендеринг в альтернативу серверному для сайтов с большим количеством страниц.
В общем хоть про что-нибудь помимо производительности или не брать в название столь обширную тему, думаю про производительность читало бы не меньше людей, чем про SEO
biomancer
Про сабсеттинг шрифтов есть один не самый очевидный нюанс - не все лицензии позволяют это делать. Иногда менять файл шрифта запрещено, в том числе для создания сабсета, поэтому стоит проверять условия лицензии перед такими действиями.