- PVSM.RU - https://www.pvsm.ru -
Мой вывод - если нужно гнаться за sео, то NextJS (React) не выбор. Мой предел - 90-93 perfomance по оценке lighthouse. Предложение - NuxtJS (Vue.js).
И вот почему я считаю что это не случайностью:
Здесь стоит сделать оговорку о том насколько разные имена здесь и то, что предположительно первым всё равно на оптимизацию, но replit и сам vercel думаю с головой писали их сайты.
Главное что нужно в sео это оптимизация event loop, который я не смог получить на NextJS. Возможно для этого нужно переписать всё приложение, но как-то руки не дошли :) Пока дропаются фреймы, отличного перфоманса не будет.
Всё же, не всем нужно 100, а на NextJS писать легко и быстро, что также нужно продукту, поэтому ниже к моим наработкам.
Pro tip: если кажется что компонент неоптимизирован, то удалите его и сделайте оценку без него, возможно дело не в нем.
NPM пакет для локального тестирования - lighthouse [1]. Намного стабильнее оценка чем в браузере. Получаете здесь стабильно 100 и вы топ! (буду ждать советов :) )
В браузере проверять в режиме Incognito. Выключает часть сбора всякой инфы и хоть на сайт напрямую это не влияет, но влияет на машину, проводящей оценки производительности.
Если остались включенные расширения с доступом в инкогнито - отключить. Влияют напрямую на загрузку сайта - могут что-то ренденрить, что-то грузить и тд. Адблокеры и гугл переводчики яркий пример.
Хром, даже делая обновление с очисткой кэша, будет что-то кэшировать и всё будет казаться куда оптимизированней. Я остановился в Firefox.
Делая оценку в браузере, после ребилда, обязательно делать новую оценку в новой вкладке, иначе часть будет закешированна.
Самый главный пункт.
Проверяем сколько шрифтов используется и уточняем у дизайнеров нужны ли они. Либо старо, либо уже неактуально, либо легко заменяется, но убирая лишние шрифты вы значительно ускоряете загрузку и упрощаете фикс CLS.
Уменьшаем количество глифов (символов) во всех оставшихся шрифтах с помощью сабсетов шрифтов. Для редактирования шрифтов мне очень был удобен fontsquirrel [2] - делаем сабсеты нужных нам языков (для любого сайта только на английском спокойно хватает latin) и жирностей.
Google Fonts не доверяем и проверяем. В css файлах уже есть ссылки на семейство с разными начертаниями и разными сабсетами (/* latin */
и /* latin-ext */
), но очень часто эти ссылки будут одинаковыми, где шрифты "всё в одном". Это не хак и весит он соответственно больше. Такое надо избегать и собирать сабсеты самим.
Быстро посмотреть сколько глифов в шрифте можно, например в консоле. Через код элемента выбираем Elements → справа, где пишутся стили, переключаем вкладку Styles на Computed → снизу написано кол-во глифов в шрифте.
Шрифты подключать через <link rel="preload"/>. Это скажется значительно меньше на TBT чем на CLS.
Шиза-чек: Убрать любые шрифты из CSS в формате base64 (если с этим конечно можно выжить). CSS файлы вырастают до гигантских размеров и начинают грузиться несколько секунд.
Для NextJS с 11-ой версии - использовать только next/script [3] (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 [4], но только для элементов появляющися по взаимодействую с юзером - бургер меню, дропдауны, попапы и тд. С настройкой { ssr: false }
результат выходил лучше всегда.
Здесь хотелось бы упоминуть next/image [5] но не буду. На момент написания статьи <Image> не принимал классов и поэтому стилизовать их невозможно было. Обход через обертки очень плох, поскольку это создает 6 DOM нод (4 div + 1 img + 1 noscript) вместо одной, что не сильно но ухудшает рендер и может встать боком, так как lighthouse требует меньше 250 нод на странице.
Любые картинки, которые не влезают в экран при открытие, использовать с атрибутом loading="lazy"
. Те что влезают обязательно использовать с width и height. Остальным картинкам они не обязательны, но желательны, так как иногда CLS может зацепить и их.
Если используется много иконок то использовать image sprites [6] - иконки закидывается в одну большую фотографию и используется через 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 [7]. Изредка может не работать и ломать картинку, но на большом количестве иконок сказывается положительно, уменьшая большую часть загрузки.
В какой-то момент нужны будут .webp, поэтому, на момент написания статьи, обязательно использование next-images [8]
const withImages = require('next-images')
module.exports = withImages({
// ... дальше обычный next.config.js
})
Обязательно проверить каждую либу - возможно она весит 30кб, используете то, что сможете переписать за 5 секунд (как у меня было со Swiper.js)
Оценка веса - bundlephobia [9]. Возможно здесь у меня жиза, но битва за каждый кб. При 10кб уже стоит задуматься. В зависимости от пакета, 1кб может выиграть 1 пункт в Perfomance, а 30кб могут выиграть 20 пунктов.
Обязательно проверьте правильно ли вы используете известные библиотеки. На гитхабе next.js [10] есть куча примеров включая популярные mobx [11] и redux [12].
Стоит напомнить про requestAnimationFrame. Если каждый фрейм что-то отрисовывается или делается проверка для рендера, то лучше использовать этот его и event loop будет чище.
Обработчики ивентов scroll, resize, touch должны работать через debounce или другой аналог. Никому точно не нужны миллион проверок и функций при каждом пикселе скрола.
Задуматься о апи запросах. Возможно у вас напрашивается batch запрос вместо тысячи единичных запросов.
Еще про апи - использовать шифрование и сжатие. Для koa это koa-compress [13]. Для Nest.js используется compression [14].
Во всех 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 [15] всегда используем с prefetch={false}. Не будет грузить вторых и третьих страниц.
Web Workers [16]. Вещь хорошая, если много логики. Не успел попробовать как сильно спасает и когда логики становится "много".
NextJS позволяет создать кастомный next/document [17], где можно теоритечески управлять загрузкой скриптов
Найденные скрипты не отличались перфомансом. Например этот [18].
Под копотом используется чистый нативный реакт, который оптимизированней с создание элементов, что достаточно влияет на перфоманс.
Какой-то очень оптимизированный скрипт загрузки стилей [19]. Не успел попробовать.
Автор: Кирилл Карпик
Источник [20]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/372334
Ссылки в тексте:
[1] lighthouse: https://www.npmjs.com/package/lighthouse
[2] fontsquirrel: https://www.fontsquirrel.com/tools/webfont-generator
[3] next/script: https://nextjs.org/docs/basic-features/script
[4] next/dynamic: https://nextjs.org/docs/advanced-features/dynamic-import
[5] next/image: https://nextjs.org/docs/api-reference/next/image
[6] image sprites: https://www.w3schools.com/css/css_image_sprites.asp
[7] SVGOMG: https://jakearchibald.github.io/svgomg/
[8] next-images: https://www.npmjs.com/package/next-images
[9] bundlephobia: https://bundlephobia.com/
[10] гитхабе next.js: https://github.com/vercel/next.js/tree/canary/examples
[11] mobx: https://github.com/vercel/next.js/tree/canary/examples/with-mobx-react-lite
[12] redux: https://github.com/vercel/next.js/tree/canary/examples/with-redux
[13] koa-compress: https://www.npmjs.com/package/koa-compress
[14] Nest.js используется compression: https://docs.nestjs.com/techniques/compression
[15] next/link: https://nextjs.org/docs/api-reference/next/link
[16] Web Workers: https://developer.mozilla.org/ru/docs/Web/API/Web_Workers_API/Using_web_workers
[17] кастомный next/document: https://nextjs.org/docs/advanced-features/custom-document
[18] этот: https://github.com/engineerapart/nextscript
[19] очень оптимизированный скрипт загрузки стилей: https://css-tricks.com/the-best-font-loading-strategies-and-how-to-execute-them/
[20] Источник: https://habr.com/ru/post/652539/?utm_source=habrahabr&utm_medium=rss&utm_campaign=652539
Нажмите здесь для печати.