Як зменшити сторінку Next.js у 3,5 раза й досягти 98 балів Lighthouse

9 хв. читання

У цій статті ми розповімо про те, як розробники Papyrus.dev виправили недоліки сайту й успішно досягли оцінки швидкодії 98 балів у Lighthouse. А ще зменшили розмір JS за першого завантаження у 3,5 раза і впровадили кілька хороших практик, як-от оптимізацію зображень.

Не будемо баритися, починаймо.

Передумови

Сайт Papyrus.dev побудовано на Next.js, фреймворку React. Він постачається з кількома зручними та розумними типовими параметрами. Крім того, тут можна з коду React створювати статичні сторінки, написані лише на HTML, CSS та JS.

Статичні сторінки можна побачити скрізь. На сторінках, які рідко змінюються, наприклад, у блогах, не варто постійно залучати до роботи сервер або отримувати з нього відповіді обсягом понад кілька сотень кБ.

В ідеалі, ми використовуємо API один раз (під час завантаження або щоразу, коли оновлюється вміст блогу), вставляємо відповідь API у HTML, об'єднуємо мінімальний набір JS та CSS, необхідних для рендерингу сторінки, і передаємо це пакет глобально у мережі CDN. Next.js спрощує це завдання.

Оскільки сайт спирається на статичні сторінки, розробники хотіли переконатися, що роблять усе можливе, аби ці сторінки були маленькі та швидкі. Тому, коли Next.js почав дорікати їм за великі розміри статичних сторінок, вони вже знали, яку оптимізацію повинні виконати:

Як зменшити сторінку Next.js у 3,5 раза й досягти 98 балів Lighthouse

Великий розмір сторінки означає повільніше завантаження, і це погано: користувачі розчаровуються, показник відмов зростає, а ще й вплив на SEO жахливий.

(~ 500 кБ JS під час першого завантаження — насправді досить невисокий показник відносно середнього значення для JS-сайтів у 2021 році. Та команда все одно вважає, що це забагато для простої сторінки блогу).

Проблеми швидкодії підтвердились не найкращими оцінками Google Lighthouse:

Як зменшити сторінку Next.js у 3,5 раза й досягти 98 балів Lighthouse

Самі лише оцінки не обов'язково визначають швидкодію сторінки, але вони можуть підказати, що поліпшити.

Аналізування пакунків

Є кілька інструментів, які підказують, на чому варто зосередитись для зменшення розміру завантаження.

@next/bundle-analyzer — один із них. Він аналізує розмір пакунків ваших компонентів Next.js, сторінок та сторонніх залежностей. Установіть його у свої dev-залежності за допомогою yarn add -D @next/bundle-analyzer і додайте таке до next.config.js, щоб запустити його за допомогою змінної середовища ANALZE:

const withBundleAnalyzer = require("@next/bundle-analyzer")({
  enabled: process.env.ANALYZE === "true",
})

module.exports = withBundleAnalyzer({{{
  // Це лише ваші звичайні параметри next.config.js. Наприклад:
  // images: {
  //  domains: ["storage.googleapis.com"],
  //},
})

Щоб почати аналіз, запустіть команду ANALYZE=true next build. Або додайте її до свого package.json новим скриптом, щоб він був завжди під рукою, за допомогою yarn analyze:

// package.json
{
  // ...
  "scripts": {
    "analyze": "ANALYZE=true next build",
  },
  // ...
}

Після запуску ви побачите звичайний процес збирання Next.js, але ваш браузер відкриється з такою барвистою сторінкою, як ця:

Як зменшити сторінку Next.js у 3,5 раза й досягти 98 балів Lighthouse

Тут є багато інформації, але в око впадає, що блок highlight.js (зокрема, мови в ньому) величезний. Розробники використовують react-syntax-highlighter, щоб позначати синтаксис у блогах, і ця функціональність залежна від highlight.js.

Що ж можна зробити для оптимізації? На щастя, react-syntax-highlighter дає змогу легко відкласти завантаження мов, поки вони не знадобляться. Це називається динамічним імпортуванням.

Динамічний імпорт

Динамічний імпорт — це можливість, додана в ES2020 (також підтримується у Next.js). Вона дозволяє динамічно завантажувати JS у середовищі виконання, можливо, на основі якоїсь умовної логіки. Наприклад ми можемо завантажити бібліотеку пошуку лише після того, як користувач натисне на поле пошуку.

Ви можете собі уявити, як завдяки цьому зменшити розміру першого завантаження сторінки: відкладаєте завантаження бібліотеки, поки сторінка не завантажиться (або, в ідеалі, поки вони не знадобляться). react-syntax-highlighter робить це ще простішим, оскільки є оператор імпорту, який відкладає завантаження мов.

Після переходу виділення синтаксису на динамічно імпортований пакунок, подивимось, як це вплинуло на розмір сторінки:

Як зменшити сторінку Next.js у 3,5 раза й досягти 98 балів Lighthouse

Досить суттєво! Це зменшило розмір динамічного маршруту (/[blogname]/[noteId]) першого завантаження JS-блогу майже у 2,5 раза. (Для домашньої сторінки блогу нічого не змінилося, але це буде виправлено далі).

Повторний запуск yarn analyze показує, що додаткових мов більше немає:

Як зменшити сторінку Next.js у 3,5 раза й досягти 98 балів Lighthouse

Непогано, але рухаймось далі.

Вилучення непотрібного коду

Під час збирання видаляються експорти, які не застосувались. Але якщо ви насправді використовуєте пакунок у коді, який нічого не робить, пакунок все одно буде об'єднано, однак він буде… нічого не робити!

Пояснимо на прикладі: під час основного рефакторингу кодової бази розробники створили логічну змінну, яка контролюватиме появу нової версії сторінки блогу. Якщо для неї встановлено значення false, показувався старий компонент, але якщо значення змінити на true, показувався новий.

Цей код мав приблизно такий вигляд:

import NoteView from "components/public/NoteView"
import NewNoteView from "components/public/NewNoteView"

// ...

return (
  <div>
     { ENV_DEPLOY_NEW == true ? <NewNoteView /> : <NoteView /> }
   </div>
)

Кількість компонентів поступово зростала, і коли для всіх них було встановлено значення true для всіх користувачів, розробники негайно …забули про цей прапор.

Це означало, що NoteView був імпортований і ніколи не використовувався. Спробуймо вилучити його і подивимось, як це вплине на розмір сторінки:

Як зменшити сторінку Next.js у 3,5 раза й досягти 98 балів Lighthouse

Знову ж таки, це значно зменшило розмір сторінок (динамічного маршруту блогу, /[blogname]) у 3,5 раза!

Стирання невживаного CSS

Також в аналізі збірки Next знайшовся масивний CSS-файл, спільний для всіх сторінок:

Як зменшити сторінку Next.js у 3,5 раза й досягти 98 балів Lighthouse

354 кБ CSS?!

Для Papyrus використано Tailwind, і з коробки він має багато класів утиліт. Якщо ви не видалите їх зі своєї робочої збірки, у вас буде великий файл CSS з купою зайвих класів.

Гляньмо до посібника Tailwind щодо оптимізації коду та додаймо запис purge до нашого файлу tailwind.config.js file:

module.exports = {
  purge: [
    "./pages/**/*.tsx",
    "./pages/**/*.js",
    "./pages/**/*.ts",
    "./components/**/*.tsx",
    "./components/**/*.js",
    "./components/**/*.ts",
  ],
  // ...
}

Якщо перезапустити yarn build, ми побачимо ще одне значне поліпшення (у розмірі CSS — інші показники, включно з першим завантаженням JS, природно, не змінилися):

Як зменшити сторінку Next.js у 3,5 раза й досягти 98 балів Lighthouse

Вимкнення попереднього завантаження

Google Lighthouse вказував на невикористаний JavaScript з JS-файлу index-c78019c9dfd0b22f4016.js.

Виявилося, що це попередньо завантажена версія домашньої сторінки Papyrus (сторінка index).

Якщо користуватися компонентом Link, наданим Next.js, він автоматично попередньо завантажує всі сторінки у вікні перегляду (або коли користувач наводить курсор на посилання). Це пришвидшує навігацію, але Lighthouse також вважає це зайвим.

Команда з розробки застосувала компонент Link для переходу на домашню сторінку через логотип, який є на всіх сторінках блогу. Існує ймовірність, що відвідувачі блогу натискатимуть на посилання, тож є сенс залишити його попередньо завантаженим, але заради оптимізації вимкнемо і його. Ми можемо це зробити за допомогою властивості prefetch:

<Link href="/" prefetch={false}>
       <Logo
         className="w-auto h-10"
       />
</Link>

Це не вплинуло на розмір вперше завантажених сторінок (дані з yarn build), але значно підвищило наші показники Lighthouse.

Оптимізація зображень

Next.js має вбудовану функцію оптимізації зображень у бібліотеці next/image та компоненті <Image />.

Це дає змогу змінювати розмір, стискати та додавати зображення в оптимізованих форматах (наприклад, WebP, якщо браузер його підтримує). Також можна відкладати завантаження зображень: вони оптимізуються і додаються, лише якщо є запит на їх отримання.

На сайті був кастомний компонент логотипу:

export default function Logo(props: Props) {
  return (
     <img src="/logo.jpg" className="h-12 w-12" />
  )
}

Ми можемо змінити його, щоб почати користуватись оптимізацією зображень:

import LogoImg from "public/logo.jpg"
import Image from "next/image"

export default function Logo(props: Props) {
  return (
     <Image src={LogoImg} priority placeholder="blur" height={12} width={12} />
  )
}

У цьому коді виконано такі дії:

  • Зображення імпортовано статичноpublic/logo.jpg). Для ефекту розмиття має бути статичний імпорт.

  • Розмиття зображення під час завантаження за допомогою властивості placeholder="blur". Коли зображення з'являється, воно має низьку роздільність і розмивається, поки не завантажиться зображення з повною роздільністю; тож ефект буде акуратний і плавний. Якщо ви імпортуєте зображення безпосередньо, це відбувається автоматично. У іншому випадку потрібно створити поле blurDataUrl.

  • Попереднє завантажене зображення за допомогою властивості priority. Вона вказує Next.js попередньо завантажувати зображення, яким ми надали високого пріоритету. Логотип з'являється у верхній частині сторінки майже на кожній сторінці, тому є сенс застосовувати попереднє завантаження.

На жаль, це не вплинуло на жодні з наших показників — ані на оцінку Lighthouse, ані на розмір наступної збірки, але, ймовірно, це пов'язано з малим розміром логотипу і невеликим обсягом (~5 кб). Якщо зробити те саме для зображень зі значно більшою роздільністю, це мало б інший ефект.

Однак тепер ми маємо ефект поступового розмиття!

Отож, гляньмо, що дали всі ці зміни.

Результати

Результати після всіх згаданих виправлень:

Як зменшити сторінку Next.js у 3,5 раза й досягти 98 балів Lighthouse
Як зменшити сторінку Next.js у 3,5 раза й досягти 98 балів Lighthouse

Це успіх! Показник швидкодії Lighthouse зріс із 79 до 98 балів, а найбільший розмір першого завантаження JS зменшився у 3,5 раза (з 468 до 181 кБ).

Цей перелік оптимізацій не є вичерпним, і, ймовірно, результат 181 кБ можна поліпшити — NextJS досі вважає сайт не оптимізованим — але вечір, витрачений на такі легкі зміни, все ж дав змогу помітно зменшити розмір сторінок.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.8K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація