Случаят на частична хидратация (с Next и Preact)

В Spring, ние сме отговорни за поддържането на различни уебсайтове за новинарски медии за нашата компания Axel Springer. Една от последните ни версии, welt.de, е най-бързият уебсайт за новинарски медии в Германия; една от най-търсените ни цели е непрекъснато да постигаме възможно най-добрата производителност и причината за това е проста: по-добрата производителност обикновено означава по-добро потребителско изживяване и по този начин по-висока задържаност на потребителите.

TL; р

Превъртете надолу до „Резюме“ за кратко обобщение с инфографика. Ключовите моменти за това накратко:
  • Производителността е от решаващо значение за мрежата
  • за постигане на висока производителност искаме да изпратим възможно най-малко на клиента
  • можем да направим това, като изберем компонентите, които искаме да изпратим и да се хидратираме до клиента
  • оставяме останалата част от страницата статична и имаме множество корени за изобразяване
  • Всичко това работи с една кодова база
  • По-долу е обширна статия за това как изпълнихме посочените по-горе стъпки. Тук също ще намерите линк към WIP репо на това внедряване:
  • https://github.com/spring-media/next-super-performance

Изпълнение в мрежата

Ако следвате Addy Osmani, вече знаете тренировката, той пише много за причината и последиците от представянето в мрежата. За да започнете, мога да препоръчам статията на Addy на тема „Цената на JavaScript през 2018 г.“. Две много важни неща, които можете да научите от тази статия са:

  • Цената на JavaScript е не само времето, необходимо за зареждане на вашия пакет
  • Времето за разбор и изпълнение на вашия JavaScript е също толкова важно

Разбира се, има много повече резултати от това, включително стратегии за зареждане, критичен път на рендериране, бюджети за изпълнение и така нататък. Всички тези неща се въртят около това как да оптимизирате каквото в крайна сметка изпратите до вашия клиент. Това, което искаме да се съсредоточим върху частичната хидратация, не е как да оптимизираме това, което изпращаш, а колко изпращаш изобщо.

Ключов аспект на това ще бъде рендерирането от страна на сървъра (SSR), защото на сървъра може да направим много, което не е необходимо да се прави на клиента. Всъщност това е основата на тази статия; Всичко, което може да се направи на сървъра, трябва да се направи на сървъра, но клиентът трябва да бъде изпратен само това, което трябва да бъде изпълнено от страна на клиента. Освен това, все още можете да приложите всичко, което знаете за уеб производителността, но ще имате много по-малко фактори за управление, това ще бъде обяснено по-подробно по-долу в статията.

SSR и хидратация

За да осъществим целта си да изградим уебсайт за изпълнение, ще използваме модифицирана версия на Next. Напред идва в комплект с много вградени функции за повишаване на производителността, най-важното е: Напред прави рендерирането от страна на сървъра (SSR) извън кутията. Това означава, че Next ще вземе приложението ви, написано в React и в този ред:

  1. Представете го като HTML низ на сървъра
  2. Изпратете предоставения HTML низ на вашите потребители като изходен код
  3. Изпратете вашия React код като JavaScript на вашите потребители
  4. И след това най-накрая „хидратирайте“ вашия HTML с помощта на вашия React код

„Хидратирането“ в този случай означава, че Next ще разгърне вашия React код над вашия HTML и след това ще каже React нещо малко по този начин:

Здравей, реагирайте, ето малко HTML, което съвпада точно с онова, което бихте изобразили, ако ви кажа да направите визуализация в празен DOM възел, моля, не предавайте всичко отново, вместо това, моля, използвайте HTML така, сякаш сте го направили и продължете с твоят ден

React ще отговори

Добре, току-що ви погледнах HTML и изглежда, че съвпада точно с това, което бих направил. Това е готино. Просто ще прикача някои обработвачи на събития към вашия DOM и вашата страница сега действа като приложение за една страница, като всичко това направих сама на първо място.

Предимствата на зареждането на уебсайт по този начин са прости: Вашите потребители вече ще виждат напълно визуализирана страница, когато заредят вашия уебсайт (вместо празна страница) и след това той ще стане интерактивен. Вашият уебсайт също ще се възползва от значително подобрение на производителността, тъй като браузърът не трябва да прави никакви пребоядисвания (повторно изобразяване на страницата ви с React).

Прекалено много режийни

Този подход е фантастичен, когато искате да създадете уеб приложения или с други думи уебсайтове, които трябва да бъдат изцяло контролирани от JavaScript и също така да са интерактивни, където щракнете. Примери за този подход в производството включват уебсайтове като Facebook, Twitter и уеб-базирани имейл клиенти.

Но повечето уебсайтове не са така, повечето уебсайтове са вид на статично съдържание и съдържат някои интерактивни елементи.

Сега накрая изпращате целия си код на приложение до потребителите си, включително компоненти React за всяко заглавие или текстов абзац навсякъде на страницата ви. Резултатът е ненужно огромен пакет, който трябва да бъде зареден, анализиран и изпълнен. Това води до неоптимална производителност, страницата ви ще бъде бавна (ер), особено за мобилни потребители и без основателна причина!

И това е гадно.

И така, какво можем да направим? Е, има много стратегии и продукти навън. Един от най-известните е Гетсби, генератор на статични сайтове (сигурен съм, че сте чували за него досега), който се фокусира силно върху оптимизацията на производителността. Проблемът с Гетсби е, че той трябва да генерира всички ваши страници и подстраници по време на компилиране, което всъщност не работи, когато имате сайтове, свързани с CMS, който се актуализира всеки ден и съдържащ милиони статии - от какво точно се нуждаем от нашите сайтове за медийни новини Ето защо ние използваме Next, както и го модифицираме, за да отговаря на нашите нужди.

Въведете частична хидратация

За да разрешим гореспоменатите проблеми, ние измислихме нещо, което обичаме да наричаме частична хидратация.

Ако сте разгледали тази тема, вероятно ще срещнете термини като прогресивна хидратация или мързелива хидратация. Те не означават конкретно едно и също нещо, но всички те са взаимосвързани и принадлежат в една и съща сфера.

Основната идея зад нашата версия за частична хидратация е: Вместо да правите SSR и след това да изпращате цялото си приложение до вашия клиент, само части от JavaScript на приложението ви ще бъдат изпратени до клиента, за да хидратира частите на вашия уебсайт, които специално изискват JavaScript за работа , Ако трябва да създадете уебсайт по такъв метод, ще имате множество малки „реагиращи“ приложения с множество корени на изобразяване на иначе статичния си уебсайт.

По този начин нещата трябва да увеличат вашия уебсайт като цяло, тъй като това, което в крайна сметка доставяте, е обикновен HTML, CSS и най-малкото JavaScript, необходимо за вашата страница. Трябва да се отбележи едно нещо, когато измервате производителността, трябва не само да взимате предвид времето за зареждане, но и да анализирате и времето за изпълнение.

Можете също така да изберете да приложите правилни стратегии за изпълнение на всичко отгоре, като разделяне на кода, грижа за бавния старт на TCP и вашия критичен път на рендеринг и т.н.

Нашата реализация

Нашата реализация се състои от 2 пакета:

  • Библиотеката на Preact за частична хидратация, наречена придружител на басейн-преактант
  • Плъгинът Next.js нарече next-super-performance

Последната библиотека е само плъгин за Next.js, който използва преглед-придружител на басейна, така че нека се концентрираме върху преакцията-придружител на басейн. Може да напиша последващ пост за следващо супер-изпълнение на някакъв етап в бъдеще.

басейн-придружител-preact

Оформление с заглавка, тяло, странична лента и 2 реактивни елемента

Представете това оформление и нека се преструваме, че сивите кутии са елементи, които могат да бъдат напълно статични и искате тези в лилаво да бъдат интерактивни. Например, по-големият може да бъде емисия в Twitter, а по-малкият - малък инструмент за гласуване. Затова трябва да приложим JavaScript към тези елементи, за да ги направим интерактивни и искаме да оставим останалите като статични елементи.

Примерно изпълнение за това с помощта на преглед на басейна може да изглежда така:

Редове 3–8 са всички компоненти, които искаме да покажем, тези компоненти ще бъдат представени на сървъра и след това изпратени като HTML и CSS на клиента, без какъвто и да е JavaScript.

Редове 10 и 11 са там, където маркираме компонентите TwitterFeed и Poll за хидратация и получаваме нов компонент в замяна. Редове 18 и 19 са мястото, където ги използваме.

Ред 22 е от изключително значение. Това е компонент, който инжектира данни за хидратация (реквизити и имена на компоненти) в страницата.

Но нека да ви обясня. Когато правим нормална хидратация с реакция, кодът ви изглежда така:

Има два проблема, които трябва да решим тук, когато правим частична хидратация

  1. ReactDOM.hydrate работи върху корен възел в DOM, възелът, който използва като начална точка за хидратацията. Този корен възел трябва да съдържа сървър, DEM дърво, което съответства на компонентите и състоянието на приложението ви. Уловът: Трябва изрично да назовете DOM възел, за да действа като корен възел. В този пример това е просто, можете да му дадете идентификатор, да използвате document.getElementbyId и след това да хвърлите този възел в ReactDOM.hydrate и сте готови!
    Частичната хидратация от друга страна означава, че ще имате множество DOM елементи на статичната си страница, които трябва да хидратирате. Не бихте искали изрично да им назовете всичко, което би било досадна работа за разработчика.
  2. Какво става, ако HydrateTwitterFeed или HydratedPoll се нуждаят от реквизити, които трябва да бъдат предадени на тях? Кажете, нещо като . Ако искаме да стартираме ReactDOM.hydrate (, rootElementOnThePage) откъде ще получим luke_schmuke? Как би могла да знае статична страница за това? Трябва по някакъв начин да ги съхраняваме и изпращаме на клиента.

Решение

Начинът, по който се справим с този проблем, може да се разбере от прилагането на withHydration:

Нека разгледаме по-подробно това: сHydration работи с помощта на техниката на компонента от по-висок ред, компонентът от по-висок ред връща оригиналния компонент заедно с оригиналните му непроменени реквизити, но също така го предразполага с