Галоўная / Артыкулы

Серверны рэндэрынг праграмы на React 18 з Express і Babel

Пераводзім праект Create React App на серверны рэндэрынг з Express і Babel: renderToString, hydrateRoot, MemoryRouter, а потым — як SSR робяць Remix і Gatsby.

Выдавец: REACTAPP.TOP7 хв чытання1712 слоў

Крыніцы: A Hands-on Guide for a Server-Side Rendering React 18 App

Рэндэрынг з бакульнай стороны для React сур’ёзна змяніўся з’явілася версія React 18. Наведзены выкладкі аднасоўваюцца да React 18, Create React App 5 і React Router 6, і выконваюцца на базе стандартнага праекта Create React App, які пашагоўна ператвараецца. Рэндэрынг павінен выконвацца на серверэ, таму патрэбны працэсы Node. Звычны Node таксама падходзіць, але nodemon ўдобны пад час розработы, таму што ён перазапускае прыложэнне кожны раз, калі змініюецца файл. Глобальная установка выглядае так:

% npm install -g nodemon

CSR проты SSR за калькамі

Пры рэндарынгу з боку кліента (CSR) прызначальнік атрымлеў майжэ порожняю HTML-сторанку, а JavaScript запалняе ўсё кантэнцтва. Першыя завантажэння можу быць повольнымі, але паслячыя навігацыі ведуцца швядка, а на серверэ ўтвараецца мінімальны навантажэння, таму што працу выконваюць бібліятэкі JavaScript. Адыянтам ёст кансалтынацыя для пошуковых сэрвісаў, адтуды што краулер не знаходзіць статычнага кантэнцтва для чытання.

Пры рэндарынгу з боку сервера прызначальнік атрымлеў готовую HTML-сторанку, якую ўжо адобразіў сервер. Гэта дае прынады для SEO, а перша адобразка ведуцца швядка. Адыянтам ёст тое, што паслячыя змены часта выклікаюць павольнае перазавантажэння всей сторанкі, што можа збільшыць навантажэння на сервер.

Калькамі дапамагаюць калькі:

  • Прыклад аплікацыі з адной сторанкай (SPA) выкарыстоўвае CSR. У замест на аддачу окрэмых HTML-файлаў па кожнам маршруту, ёна дынамічна адрабатвае кожны маршрут унутры прызначальніка.
  • Універсальны JavaScript працюе як на кліентскай, так і на серверскай стороне. Ён адражоўвае HTML у браузеры як SPA, адражоўвае той самы HTML на серверы і надае яго браузеру.
  • Код React за сваёй прыродай ўніверсальны, таму код, напісаны для CSR, можа вікорыстоўвацца для SSR за дапамогою той самай базы коду.
  • SSR насправдзе ўжо існаваў ранейш за CSR. Ён вернуўся у савэцкай форме, пабудованай на універсальным JavaScript, і самэ гэта зараз зазвычай маюць на увазе, калі говораць пра SSR.

    Базавая аплікацыя CSR

    Створыце новы праект:

    % yarn create react-app react-ssr
    % cd react-ssr
    

    Пачатакніце з npm start. У вікні прыгледу браузера Elements спінаючыся логаўк і тэкст паказваюцца як HTML, створаны JavaScript.

    Гэта класычны прыклад CSR: скрыпты ствараюць тэкстовы кантэнт HTML. Вікно Network паказвае, што на самай працэ ўсунуў сервер.

    У тэле знаходзіцца пакет JavaScript, а не рэальны кантэнт, таму для індэксавання у роўеру практычна няма нічы значныя.

    Распаўненне версіі для рэальной працы

    Для SSR патрэбна версія для рэальной працы, таму першы крок — стварэнне невялікага сервера Express, які распаўняе скомпіляваны выход React. Раней опісаная настаўка React, гатовая да рэальной працы, стварае падставу. Файл сервера бывае двух варыянтаў: адны ёсць у фармаце CommonJS, server/index.js, а другі — у фармаце ES module, server/index.mjs.

    Выконайце npm run build, ў рэзультаце чаго будзе створана збірка, а пасля запусціце сервер за дапамою nodemon server або nodemon server/index.mjs. Карточка Network цяпер паказвае адну збірку JavaScript (лінія 12) і порожню маркап-тэкстуру (лінія 17). Яшчо не відбываецца нічога, што б сервер генераваў, таму гэта яшчо ёсць CSR.

    Тры крокі для SSR у Express

    Ёжы SSR у серверэ Express трэба адбыць за тры крокі.

    Шаг 1: гідратацыя на кліенце. ReactDOM.hydrateRoot() падобны ReactDOM.createRoot(), з сігнатурой hydrateRoot(container, element[, options]). Ён прызначаны для кантэйнера, чыё HTML ужэ было створана ReactDOMServer. Паколькі вузел ужэ мае маркап, выроблены серверам, React яго застаўляе і толькі прыўязвае апрацоўшчыкі змаганняў, што спрыяе шырокаму завантажэнню. Выклік знаходзіцца на 7-й лініі src/index.js.

    Шаг 2: рэндарыўванне на серверэ. React поставляе об’ект ReactDOMServer, каб ператворыць компоненты на статычны маркап, тады браузер отрымае сторанку, якая ужо заполнена дадзеннямі. Паколькі код React — універсальны JavaScript, тыя ж самыя компоненты працуюць як на кліенце, так і на серверэ.

    Шаг 3: адпаведнае рашэнне спецыфічных патрабаванняў сторанкі. Кожны маршрут можа выкарыстоўваць свае сабэйскія патрабавання, як будзе паказана ў наступных раздзелах.

    Як параджуць Node разумець JSX

    Ўсё большае колька пакетаў можа запусціць SSR. @babel/register ўваходзіць у ліст самых простых. З’явіце яго разам з babel-plugin-transform-assets:

    % yarn add -D @babel/register
    % yarn add -D babel-plugin-transform-assets
    

    Оба знаходзяцца ў devDependencies:

    "devDependencies": {
      "@babel/register": "^7.18.9",
      "babel-plugin-transform-assets": "^1.0.2"
    }
    

    @babel/register скомпілюе файлы на лету, і яго можна налаштаваць для трансформацыі JSX і рэсурсаў. Існуе адна праблема: ён не можа скомпілюваць натыўныя ES-модулі Node.js на лету, адтолькі ўсё яшчэ не існуе стабільнага API для падключэння да працэсу завантажэння ES-модуліў. Самэ гэтая прычына змусiла ў эксперыментах з SSR вжываць файлы CommonJS.

    React 18 перэрабіў свае серверныя API і пераказаў іх пад react-dom/server. Функцыя renderToString яшчэ працюе, але ў яё падтрымцы Suspense ёсць абмежэння. Новыя API, у тым часе renderToPipeableStream, абоўсюдна падтрымліваюць Suspense на серверы, а таксама практыку стрымінгу у рамках SSR.

    Прагляд сервера SSR

    Апошню версію файла server/index.js паспяшна пояснюецца ніжэй па номерамі лініяў.

    • Лініі з 1 да 15 налаштовуюць Babel за дапамою хука require, тады файлы скомпілююцца праз адразу ж ў момент завантажэння.
  • У другій лініі выбіраюцца два налаштавання. @babel/preset-env дозволяе коду вікорыстоўваць найновейшы JavaScript без неабяжнасці ручнага выбірання трансформацый сінтаксісу для кожнай мэтавай асяроддзя. @babel/preset-react автаматычна імпортуе функцыі, у якія компілюецца JSX.
  • Лініі з 3 па 14 налаштовуюць babel-plugin-transform-assets, які візначае, як ператвараюцца статычныя медыя-файлы. Без яго таг svg спрычынае памылку SyntaxError: Unexpected token '<'.
  • У 16-й лініі трэбуецца React, у 17-й — ReactDOMServer, а у 18-й — стандартны экспорт з src/App.js.
  • Тут важна парадыгма выканання коду. Каментар app.use() на 47-й лініі павінен выканацца пасля app.get() на лініях 25–45. Якбы ён выканаўся першым, ён бы сэрвірав статычныя файлы, укладзеныя ў index.html, для корневага маршруту, і app.get() ніколі бы не запрацаваў.

    На 26-й лініі фіксуецца URL кожнага запіту. Для праекта Create React App спіс выглядае так:

    Request URL = /
    Request URL = /static/js/main.b92cc87b.js
    Request URL = /static/css/main.073c9b0a.css
    Request URL = /static/media/logo.06e73328.svg
    Request URL = /static/css/main.073c9b0a.css.map
    Request URL = /static/js/main.b92cc87b.js.map
    Request URL = /static/media/logo.6ce24c58023cc2f8fd88fe9d219db6c6.svg
    Request URL = /favicon.ico
    Request URL = /manifest.json
    Request URL = /logo192.png
    

    У рэях 27–29 праблема ў тым, што app.get() адрасавае толькі корневы маршрут, а статычныя рэсурсы пераходзяць да наступнага мідлвэра ў рэе 47.

    У рэе 30 вызначаецца ReactDOMServer.renderToString(element), каб стварыць HTML на серверэ. Было бы прыродна напісаць ReactDOMServer.renderToString() безпосерэдзя, але тады JSX павінен быў бы знаходзіцца ў іншым файле, чым той, дзе налаштавана функцыя @babel-register, таму і выкарыстоўваецца окрэмы элемент.

    У рэе 31 выводзіцца маркап, створаны серверам:

    <div class="App"><header class="App-header"><img src="static/media/logo.06e73328.svg" class="App-logo" alt="logo"/><p>Edit <code>src/App.js</code> and save to reload.</p><a class="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer">Learn React</a></header></div>
    

    Лінія 33 завантажае файл index.html для рэлізу, а лініі 34–44 чытаюць яго ўмест. Якшто няма памылак, створаны маркап (лінія 42) дадаецца ў коранавы тэг, і готовы index.html стае адказам на першы запіт.

    Перакананне ў правямальнасці

    Запускайце nodemon server і зноў адзірніце вкладку Network. Завантажаны дакумент цяпер мае маркап, створаны серверам. Лініі 17–27 тэла адпаведаюць цэлай сторанцы, што дае інструментам SEO чаго-то рэальна гледзець.

    Для болей строгага тэсту ачыніце chrome://settings/content/javascript і выключыце JavaScript.

    Сторанца продовжае атрыбутавацца без JavaScript.

    Гэта і ёсь SSR.

    Дадаць маршруты

    Аплікацыя з адной сторанкай — гэта проста, таму наступны вопыт стосуецца калькі маршрутаў. Пачніце з інсталлявання react-router-dom:

    % yarn add react-router-dom
    

    Ён прыяўляецца да dependencies:

    "dependencies": {
      "react-router-dom": "^6.11.2"
    }
    

    Пасля чаго зменіце src/App.js. У 1-й лініі імпортуецца React, які неабяжна для SSR. У 2-й лініі імпортуецца MemoryRouter замест BrowserRouter. Браузерны рутэр выкарыстоўвае HTML5 pushState history API, які Node.js не падтрымляе. MemoryRouter зберагае історыю URL у памяці, не паўтараючы дзеянні ў стрэлцы адрэсы, што падходзіць для тэстаў і сэрвэраў, якія не ўтвараюць браузер. Іншы варыянт — StaticRouter з react-router-dom/server, але ён не падходзіць для складных маршрутаў. У лініях 34-40 выкарыстоўваецца MemoryRouter.

    Зноў запусціце працэс будавання за дапамою npm run build, запусціце nodemon server і адвідавайце http://localhost:8080.

    SSR працюе, але ёсць адна проблема: URL у стрэлцы адрэсы не адкладзіцца.

    Дзе самастайна рэалізаваны SSR стае складным

    Рутаванне працюе з гэтым абяканнем, а іншыя проблемы яшчэ чакаюць рашэння, такія як запрашанне дадзейнаў і Redux. Работа на стороне сервера ёсць менш простай, чым на стороне кліента, і кожная старонка можа патрабаваць спецыяльнага адносу.

    Няма прычыны рашаць усё гэта вручную, адколі калькі фрэймворкаў і бібліятэкаў уключаюць SSR. Remix — гэта фрэймворк для веб-праектаў усіх рангаў, які выходзіць з фундаменталей веба і працюе да інтэрфейсу корыстніка, уключаючы SSR і іншыя функцыі, без зайвага коду. Код кліента вызывае hydrateRoot на 4-й лініі, а код сервера — renderToPipeableStream на 4-й лініі.

    У React 18 была адаптавана функцыя renderToPipeableStream(element, options). Як толькі яна прабуе працаваць з элементам React, яна стварае пачатковы HTML і вяртае стрым. У гэтым стрыме ёсць метод pipe(res) для перадачы рэзультата далей і метод abort() для анулювання запіта. Падтрымка функцыі Suspense ўсёвыключна повная, а HTML з запазджаным контэнтам выдаўаецца у віглядзе стрыма без пазнейшага „прыявлення“ контэнту, так як адразу ж вставляюцца тэгі <script>, якія запалняюць прызначаны прастор.

    У вікні Network тэла староні-індэксу Remix ужо мае цэлую сторону, таму гэта ёсць SSR.

    Gatsby, Next.js, NuxtJS, Quasar і SvelteKit таксама прыносяць падтрымку SSR і супаўзвязаныя функцыі без дадатковага коду, хоча кожны з іх адначасова падае ўвагу на SSR трохі інакш. На староне-індэксе Gatsby ў вікні Network таксама паказваецца цэлая структура стороны.

    Gatsby — гэта быстрый, надзямны генератор статычных сайтаў, які выкарыстоўвае React для атракцыя статычнага кантэнту, і ён дазволяе зменіць статычны HTML пад час яго атракцыя на серверэ.

    SSR чы статычная атракцыя

    Калі сторана містыць толькі статычныя даны, статычная атракцыя ўсё прыгожэй. Якшча адпаведзь ёсць дынамічная, тады лепшы варант — SSR. Інодзе найлепей падходзіць гібрыдны падход. SSR выконваецца за запатраннем, калі толькі запрашуецца файл, тады як статычная атракцыя выконваецца раз у час пабудовы. І адна, і другая методы ёсць прыемныя для SEO, таму што у обох случаях кантэнт знаходзіцца ў тэле HTML.

    Падсумак

    Настройка SSR для Create React App складаецца з трох элементаў:

    1. Атракцыя маркапаў, атракцыйных на серверэ, за дапамогай ReactDOM.hydrateRoot().
    2. Атракцыя компанентаў да статычных маркапаў за дапамогай об’екта ReactDOMServer, чыяі API падтрымліваюць як SSR, так і статычную атракцыю.
  • Бяцець кі ўтлакае кожнай сторанцык спецякікськія потрэбы.
  • Для рэальнага праектуяглыкіе вярны выбор — фрамвэрк або біблекае, які ужо гэта рэгліяе.

    Want to Connect?
    
    If you are interested, check out my directory of web development articles.