Порівнюємо способи генерації сторінок: CSR, SSR, SSG, ISR. Гайд на основі стеку React

Усі статті, обговорення, новини про Front-end — в одному місці. Підписуйтеся на телеграм-канал!

Всім привіт. Мене звати Максим Рудний і я займаюсь комерційною розробкою програмного забезпечення уже понад 10 років, працюю Front-end розробником. За цей час веброзробка пройшла декілька етапів розвитку, і на сьогодні, з точки зору front-end, в основному ми використовуємо різноманітні JavaScript фреймворки та створюємо так звані односторінкові сайти — Single Page Application (SPA).

Разом з цими SPA в нашому житті з’явились декілька цікавих концепцій, які початківцям можуть здатися складними та, як мінімум, незрозумілими. В чому різниця між клієнтською, серверною, статичною та інкрементальною генераціями сайтів? Та що воно таке взагалі і яким боком це стосується пошукової оптимізації сайтів (SEO)?

Пропоную ознайомитися з наступними концепціями та розглянути їхні переваги і недоліки:

  • Client-Side Rendering (CSR);
  • Server-Side Rendering (SSR);
  • Static Site Generation (SSG);
  • Incremental Static Regeneration (ISR).

Даний матеріал буде корисним Front-end розробникам будь-якого рівня, адже ми розглянемо саму концепцію, плюси та мінуси кожного підходу, а також інструменти, якими можна реалізувати їх. За основу я візьму React стек.

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

Трішки історії

В древні часи, які більшість junior та middle розробників уже і не пам’ятають — десь років 10 тому, — не було ніяких Реактів чи Ангулярів і всі сайти були «багатосторінкові» додатки. Ми відкривали адресу сайту в браузері, йшов запит на сервер, де якийсь умовний PHP генерував нам цілу сторінку (весь HTML, CSS та трішки JS) і повертав клієнту. Коли ми натискали на посилання на іншу сторінку, знову слали такий же запит, сервер генерував нову сторінку і повертав її. Браузер видаляв стару та рендерив нову, при цьому відбувався такий собі «блік» при переходах між сторінками. Даний підхід називається Multi page application (MPA).

З точки зору SEO — це ідеальний підхід. Ми маємо повну сторінку з усією необхідною інформацією і будь-який пошуковий бот відразу її зчитував, наші сайти в топі пошукової видачі, клієнти (власники цих сайтів) — щасливі.

З точки зору користувачів сайтів — не все так ідеально. При відкритті кожної наступної сторінки ми чекаємо, доки завантажаться всі ресурси заново та відрендериться сторінка. Звісно, більшість ресурсів уже закешовані, але все одно очікуємо повного відмалювання сторінки. Ну, і бліки при переходах ніхто не скасовував.

Революцією в цьому підході стала поява фреймворків, які дозволяли реалізовувати так звані односторінкові сайти — Single Page Application (SPA). Суть в тому, що ми один раз завантажуємо CSS, JS та практично порожню HTML-сторінку, і потім JS уже будує все в клієнтському браузері. При переході між сторінками JS перебудовує лише ту частину, яка змінилась, і при цьому, за потреби, робить AJAX запит на сервер, щоб отримати необхідні дані. Ми позбулися бліків!

MPA vs. SPA

Клієнти задоволені — один раз завантажуємо ресурси й усі сторінки плавно відображаються в браузері. Користувацький досвід схожий на досвід використання мобільних нативних додатків.

Скажу навіть більше, якщо усі ресурси уже завантажені, чому б їх не зберігати на телефоні та користуватися навіть при відсутності інтернету? Презентую вам Progressive Web Apps. Не всім же додаткам потрібно постійно завантажувати актуальну інформацію з сервера, є ж різноманітні ігри або статичні додатки. Та навіть якщо і потрібно завантажувати інформацію, наприклад, статті, їх можна завантажити наперед і зберегти разом з файлами.

Реалізувати даний механізм допоможе service worker. І, як бонус, зберігши додаток на телефон вручну (і так можна), він працює без відображення навігації браузера, як звичайна програма встановлена з App Store чи Google Play.

SEO ж страждає. Пошукові боти завантажують сторінку, а там — нічого нема, пусто. Виконувати JS вони не вміли, тому й отримати таку ж сторінку, як користувачі, не могли. До того ж URL-адреса не змінювалась.

Приклад сторінки, яку бачив пошуковий бот

З часом URL-адресу почали змінювати при переході між сторінками. Спочатку це була хеш-навігація (за допомогою символу #, JS дозволяв змінювати хеш в адресі), потім повноцінна зміна адреси за допомогою HistoryAPI. Деякі пошукові боти почали виконувати JS, але це не було вирішенням. Рендеринг повноцінної сторінки — довгий та затратний процес. Кожен бот має так званий «бюджет» — обмеження по часу, ресурсах та кількості сторінок, які він може просканувати. Уявіть лише, скільки сторінок Google індексує кожного дня — шалений обсяг роботи.

Розв’язання проблеми SEO став серверний рендеринг. Прийшов час глянути на нього детальніше, але спочатку закриємо питання з клієнтською генерацією сторінок.

Клієнтська генерація сторінок — Client-Side Rendering (CSR)

Підхід доволі простий, особливо в наші дні. Створений додаток складається з кількох файлів: файл стилів, скрипти та умовно порожня HTML-сторінка, в якій підключаються стилі та скрипти. У файлі index.html з тегів є лише один div з id="root«, де і будується вся сторінка. При переході між сторінками змінюється URL-адреса в браузері та підтягуються дані з сервера, за потреби. Реалізація проста, на сервер навантаження мінімальні, адже не потрібно рендерити сторінку на кожен запит, а лише повернути статичні файли. Якщо потрібні дані, можна створити окремий RESTful сервер, який повертатиме дані в JSON форматі.

Як бонус — легкість масштабування, і дані можуть використовуватися різними клієнтами (вебсторінки, мобільні додатки, інші сервера). До того ж можна зекономити на хостингу. Вам не потрібно багато ресурсів, щоб повернути декілька файлів.

Звісно, в даному підході страждає SEO. Додатково можуть виникнути проблеми при спробі поділитися такими сторінками в соціальних мережах, адже їм також потрібно завантажити сторінку, відрендерити її та знайти потрібні дані. Ну, і не забуваємо про користувачів, якщо в них старі слабенькі телефони, то рендеринг сторінки займе цілу вічність.

✅ Переваги:

  • легкість реалізації;
  • менше навантаження на сервер;
  • актуальність інформації;
  • можна обійтися без сервера;
  • легкість масштабування;
  • дешевший хостинг.

❌ Недоліки:

  • страждає SEO;
  • social media crawlers та проблеми з share;
  • більше навантаження на пристрої клієнтів;
  • розмір js файлу при першому завантаженні.

⚙️ Інструменти:

Серверна генерація сторінок — Server-Side Rendering (SSR)

Головна особливість цього підходу в тому, що вся сторінка рендериться на сервері при першому запиті, а потім уже в браузері ініціалізується React, і далі додаток працює як звичайний SPA, виконуючи, за потреби, AJAX-запити на сервер. Дані завантажуються завжди актуальні, адже при кожному перезавантаженні сторінки відбувається запит на сервер. З точки зору SEO все також ідеально: повертається повноцінна сторінка. Користувачі також задоволені — працюють зі звичайним SPA, різниці вони не бачать.

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

З даним підходом виникають деякі труднощі. Якщо реалізовувати серверний рендеринг самотужки за допомогою NodeJS, то потрібно:

  • запустити React на сервері, щоб він повернув згенерований компонент. В React з коробки є функція ReactDOMServer.renderToString() яка це робить;
  • передати правильну URL-адресу сторінки, сервер же ж не браузер. Використаємо статичний роутер — <StaticRouter location={req.originalUrl } >;
  • заздалегідь отримати дані та відрендeрити додаток з їхнім використанням;
  • після завантаження сторінки запустити React, щоб ми могли переходити між сторінками, як і при клієнтському рендерингу. Для цього використовується функція ReactDOM.hydrate() (ReactDOM.hydrateRoot() для нової версії React). На відміну від ReactDOM.render() вона не перетирає весь вміст контейнера, а лише пробує до заздалегідь згенерованого HTML додати React event listeners.

✅ Переваги:

  • ідеальна реалізація SEO;
  • актуальність інформації;
  • швидкість завантаження та відображення контенту (Time to Interactive);
  • налаштування кеша на сервері.

❌ Недоліки:

  • складність реалізації;
  • навантаження на сервер;
  • збільшення розміру файлів що завантажуються з сервера;
  • довша відповідь від сервера (TTFB — Time to first byte).

⚙️ Інструменти:

Статична генерація сайтів — Static Site Generation (SSG)

Чи потрібно нам кожен раз на сервері генерувати цілу сторінку, якщо дані не так часто оновлюються? Для різних лендінгів, сайтів-візиток, блогів, простих інтернет-магазинів, де інформація оновлюється дуже рідко, підтримка серверного рендерингу може бути накладною та надлишковою.

Тут на сцену виходить SSG. Навіщо виконувати генерацію на кожен запит, якщо можна один раз при побудові додатку згенерувати усі сторінки. Просто складаємо в папку всі HTML (CSS та JS) файли, розміщуємо їх на простенькому файловому сервері чи взагалі в CDN і повертаємо при кожному запиті. Повна відсутність сервера для обробки запитів чи генерування сторінок. Отримуємо максимальну швидкість завантаження та безпеку. Безпечний сервер — той сервер, якого нема.

З точки зору SEO — це ідеальний варіант. Для користувача сторінка відображається ще швидше ніж при SSR, ми не очікуємо дані на сервері, тому TTFB менший. Для користувачів все залишається незмінним, адже після завантаження сторінки там знову ініціалізуються React і додаток працює, як звичайний SPA.

Недоліком даного підходу являється те, що при оновленні даних потрібно повністю перезібрати проєкт і відрендерити всі сторінки. Я б не назвав це недоліком, все так, це більше особливість. Якщо сайт містить близько 100 000 сторінок, то генерація може зайняти декілька годин. На це потрібно звернути увагу.

Також може виникнути питання: а як щодо взаємодії користувача з сайтом? Що робити, якщо потрібно відправити якусь форму або зібрати інформацію? Ніхто не забороняє запустити окремий RESTful сервер, який відповідатиме за обробку різноманітних запитів від клієнтів. Також можна звернути увагу на Serverless або «хмарні функції», рекомендую глянути на AWS Lambda або GCP Cloud functions.

✅ Переваги:

  • ідеальна реалізація SEO;
  • швидкість завантаження та відображення;
  • надійність та безпека (нема сервера);
  • дешевий хостинг.

❌ Недоліки:

  • при оновленні контенту потрібно перезбирати проєкт;
  • час на генерацію великої кількості сторінок, наприклад для 100 000 продуктів.

⚙️ Інструменти:

Інкрементальна регенерація статичних сторінок — Incremental Static Regeneration (ISR)

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

Рішенням буде розділення сторінок. Ті, що частіше оновлюються, будуть рендеритися за допомогою SSR, ті, що рідко оновлюються, будуть згенеровані за допомогою SSG.

✅ Переваги:

  • ідеальна реалізація SEO;
  • швидкість завантаження та відображення;
  • можна застосовувати для деяких сторінок;
  • відносна актуальність інформації.

❌ Недоліки:

  • складність реалізації;
  • необхідний сервер.

⚙️ Інструменти:

👍ПодобаєтьсяСподобалось58
До обраногоВ обраному30
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Коментар порушує правила спільноти і видалений модераторами.

Коментар порушує правила спільноти і видалений модераторами.

Недоліки:

при оновленні контенту потрібно перезбирати проєкт;

nextjs.org/...​/functions/revalidatePath — проблема вирішена

крута стаття та все докладно розжовано!

Хороша та зрозуміла стаття для новачків, дякую.

Дуже гарна та детально написана стаття

Гарна стаття, дає загальне розуміння теми.

Уточнення: здається в розділі про SSR автор переплутав TTI та FCP/LCP (First/Largest Contentful Paint). При SSR підході TTI як раз доволі суттєвий, оскільки інтерактивності передує регідрація, яка займає час, — іноді ДУЖЕ суттєвий. Нормальних механізмів вирішення цього питання сам Реакт не пропонує, але є певні НЕідеальні костильні підходи — гуглити «react hydration on demand» або «react lazy hydration». Можливо в більш пізніх версіях 18-ого ріакту, або в 19 з’явиться щось з меншим оверхедом.

You might also like:
10 Rendering Patterns for Web Apps
youtu.be/Dkx5ydvtpCA 👀

Корисна інформація! Дякую за статтю!

Якраз хотів про це почитати, дякую!
Ставлю вподобайку :)

Дуже гарний огляд. Сам використовую Vue+11ty+headlessCMS, з рендерінгом компонентів під час зборки, зі зборкою доводиться трохи гратися бо багато сторінок, але це дозволяє повністю взяти контроль в свої руки.

Чудова стаття, дякую за витрачений час!

Щодо недоліків статичної генерації сайтів: якщо треба генерувати 100К сторінок, то треба використовувати швидкий генератор типу Hugo, який впорається з цією операцією не за години, а за хвилини. Із тих, що я пробував генераторів SSG, Hugo найшвидший.

З точки зору користувачів сайтів — не все так ідеально.

Як раз такі для користувачів ідеально — тому ще це «нативно», це природня поведінка браузеру. Вебсайт — це набор перелінкованих сторінок.

От з точки зору DX — так, не ідеально.

Дуже гарна стаття — стисло та змістовно! Дякую!

десь років 10 тому, — не було ніяких Реактів чи Ангулярів і всі сайти були «багатосторінкові» додатки

Це в 2013 році то? В 2010 вже релізнувся Backbone, в 2011- Ember, а взагалі клієнтський рендерінг завертівся з ajax та JQuery в 2005-2006 роках і потім Dojo/Prototype.js/MooTools/Ext.js.

Напевно йшлось що їх не так часто викрористовували

Непогано викладено, структуровано і зрозуміло. Дякую за гарний допис.

Я б ще трошечки розписав про «складність реалізації» SSR. Бо якщо ми то не будемо робити руками, то той самий Next.js зробить все за нас, і складність то вже «розщеплення стану» між сервером і клієнтом, бо коли ми пишемо логіку, то там буде найбільше підводного каміння. Next 13 beta app directory підхід наче виправляє цей недолік, але воно покищо в беті.

Все чьотко й в тему. Дякую ;)

Коментар порушує правила спільноти і видалений модераторами.

Дуже цікаво і добре складена стаття
Продовжуйте!

Підписатись на коментарі