Автор оригинала: FreeCodeCamp Community Member.
Давайте посмотрим на функцию, которая позволяет строить Универсальный Приложения с Реагировать Отказ
Рендеринг Server-Side – SSR отсюда на – это способность Framework Framework Чтобы сделать разметку во время работы на Задняя система Отказ
Приложения, которые имеют возможность рендеринга как на сервере, так и на клиенте, называются Универсальные приложения Отказ
Зачем беспокоиться?
Чтобы понять, почему требуется SSR, нам нужно понять эволюцию веб-приложений за последние 10 лет.
Это тесно связано с подъемом Приложение одно страницы – SPA отсюда на Отказ СПА предлагает большие преимущества в скорости и UX по традиционным приложениям, оказываемым сервером.
Но есть поймать. Начальный запрос сервера, как правило, возвращает Пустой HTML Файл с букетом ссылок CSS и JavaScript (JS). Затем необходимо выбрать внешние файлы, чтобы регларировать соответствующую разметку.
Это означает, что пользователю придется ждать дольше для Первоначальный рендер Отказ Это также означает, что скалы могут интерпретировать вашу страницу как пустую.
Таким образом, идея состоит в том, чтобы сделать ваше приложение на сервере первоначально, затем использовать возможности SPA на клиенте.
SSR + приложение *
* Вы найдете термин Изоморфное приложение В некоторых статьях – это то же самое.
Теперь пользователь не должен ждать, пока ваш JS загрузится и получает полностью оказывается HTML Как только первоначальный запрос возвращает ответ.
Представьте себе огромное улучшение для пользователей, навигацию на медленных сетях 3G. Вместо того, чтобы ждать более 20 лет для загрузки сайта, вы получаете контент на своем экране почти мгновенно.
И теперь все запросы, которые сделаны для вашего сервера, возвращают полностью отображаемые HTML. Отличная новость для вашего SEO отдела!
Crawlers Теперь увидит ваш сайт как любой другой статический сайт в Интернете и будет индекс Все контент, которое вы визуете на сервере.
Итак, чтобы повторить, два основных преимущества, которые мы получаем от SSR:
- Быстрее для начальной страницы визуализации
- Полностью индексируемые HTML-страницы
Понимание SSR – один шаг за раз
Давайте возьмем итерационный подход к созданию нашего полного примера SSR. Начнем с API Rection для рендеринга серверов, и мы добавим что-то в микс на каждом шаге.
Вы можете следовать Этот репозиторий И теги определены там для каждого шага.
Основная настройка
Первые вещи в первую очередь. Для использования SSR нам нужен сервер! Мы будем использовать простой Экспресс Приложение, которое окажет наше приложение React.
import express from "express"; import path from "path"; import React from "react"; import { renderToString } from "react-dom/server"; import Layout from "./components/Layout"; const app = express(); app.use( express.static( path.resolve( __dirname, "../dist" ) ) ); app.get( "/*", ( req, res ) => { const jsx = (); const reactDom = renderToString( jsx ); res.writeHead( 200, { "Content-Type": "text/html" } ); res.end( htmlTemplate( reactDom ) ); } ); app.listen( 2048 ); function htmlTemplate( reactDom ) { return ` React SSR ${ reactDom }`; }
Нам нужно сказать Express для обслуживания наших статических файлов из нашей выходной сети – строка 10.
Мы создаем маршрут, который обрабатывает все нестатические входящие запросы. Этот маршрут ответит с помощью визуализации HTML.
Мы используем redertostring
– строки 13-14 – для преобразования нашего запуска JSX в строка
Что мы вставляем в шаблон HTML.
В качестве примечания мы используем одними нужные плагины Babel для кода клиента и для сервера. Итак, Jsx и ES модули работать внутри server.js
Отказ
Соответствующий метод на клиенте сейчас Reactom.hydrate
Отказ Эта функция будет использовать приложение React Read RACT и прикрепит обработчики событий.
import ReactDOM from "react-dom"; import Layout from "./components/Layout"; const app = document.getElementById( "app" ); ReactDOM.hydrate(, app );
Чтобы увидеть полный пример, проверить Базовый
Тег в Репозиторий Отказ
Это оно! Вы только что создали свой первый Представленные сервер Реагирование приложения!
Реагнийный маршрутизатор
Мы должны быть честны здесь, приложение не делает много. Итак, давайте добавим несколько маршрутов и посмотрим, как мы обрабатываем часть сервера.
import { Link, Switch, Route } from "react-router-dom"; import Home from "./Home"; import About from "./About"; import Contact from "./Contact"; export default class Layout extends React.Component { /* ... */ render() { return (); } }{ this.state.title }
Home About Contact
Макет
Компонент теперь отображает несколько маршрутов на клиенте.
Нам нужно имитировать настройку маршрутизатора на сервере. Ниже вы можете увидеть основные изменения, которые должны быть сделаны.
/* ... */ import { StaticRouter } from "react-router-dom"; /* ... */ app.get( "/*", ( req, res ) => { const context = { }; const jsx = (); const reactDom = renderToString( jsx ); res.writeHead( 200, { "Content-Type": "text/html" } ); res.end( htmlTemplate( reactDom ) ); } ); /* ... */
На сервере нам нужно обернуть наше приложение React в StaticRouter
компонент и предоставить Расположение
Отказ
Как боковая заметка, контекст
используется для отслеживания потенциальных перенаправлений при рендеринге RACT DOM. Это должно быть обработано с 3xx ответ с сервера.
Полный пример можно увидеть на Маршрутизатор
Тег в тот же репозиторий Отказ
Redux.
Теперь, когда у нас есть возможности маршрутизации, давайте интегрируем Redux Отказ
В простом сценарии нам нужно Redux, чтобы обрабатывать управление государством на клиенте. Но что, если нам нужно сделать части дома на основе этого состояния? Имеет смысл инициализировать Redux на сервере.
Если ваше приложение это Отправка Действия на Сервер это нужно захват состояние и отправьте его на провод вместе с HTML. На клиенте мы кормите это начальное состояние в redux.
Давайте сначала посмотрим на сервер:
/* ... */ import { Provider as ReduxProvider } from "react-redux"; /* ... */ app.get( "/*", ( req, res ) => { const context = { }; const store = createStore( ); store.dispatch( initializeSession( ) ); const jsx = (); const reactDom = renderToString( jsx ); const reduxState = store.getState( ); res.writeHead( 200, { "Content-Type": "text/html" } ); res.end( htmlTemplate( reactDom, reduxState ) ); } ); app.listen( 2048 ); function htmlTemplate( reactDom, reduxState ) { return ` /* ... */ ${ reactDom }/* ... */ `; }
Это выглядит уродливым, но нам нужно отправить полное состояние JSON вместе с нашим HTML.
Тогда мы посмотрим на клиента:
import React from "react"; import ReactDOM from "react-dom"; import { BrowserRouter as Router } from "react-router-dom"; import { Provider as ReduxProvider } from "react-redux"; import Layout from "./components/Layout"; import createStore from "./store"; const store = createStore( window.REDUX_DATA ); const jsx = (); const app = document.getElementById( "app" ); ReactDOM.hydrate( jsx, app );
Обратите внимание, что мы называем Createstore
Дважды, сначала на сервере, затем на клиенте. Однако на клиенте мы инициализируем состояние с любым состоянием на сервере. Этот процесс похож на гидратацию DOM.
Полный пример можно увидеть на redux
Тег в тот же репозиторий Отказ
Привлечь данные
Окончательный кусок головоломки загружает данные. Вот где он становится немного сложнее. Допустим, у нас есть API, обслуживающая данные JSON.
В нашей кодовой базе я получаю все события с 2018 года Формулы 1 сезона от общественного API Отказ Допустим, мы хотим показать все события на Главная страница.
Мы можем позвонить нашему API только от клиента после установки приложения RACT, и все отображается. Но это будет плохое влияние на UX, потенциально показывать спиннер или погрузчик до того, как пользователь видит соответствующий контент.
У нас уже есть Redux, как способ сохранения данных на сервере и отправлять его на клиента.
Что, если мы сделаем наши вызовы API на сервере, храните результаты redux, а затем рендерируйте полный HTML с соответствующими данными для клиента?
Но как мы можем знать, какие звонки должны быть сделаны?
Во-первых, нам нужен другой способ объявления маршрутов. Таким образом, мы переключаемся на файл конфигурации так называемых маршрутов.
export default [ { path: "/", component: Home, exact: true, }, { path: "/about", component: About, exact: true, }, { path: "/contact", component: Contact, exact: true, }, { path: "/secret", component: Secret, exact: true, }, ];
И мы статиственно объявляем требования к данным на каждом компонент.
/* ... */ import { fetchData } from "../store"; class Home extends React.Component { /* ... */ render( ) { const { circuits } = this.props; return ( /* ... */ ); } } Home.serverFetch = fetchData; // static declaration of data requirements /* ... */
Имейте в виду, что Serverfetch
Составлено, вы можете использовать все, что звучит лучше для вас.
Как отмечают здесь, Fetchdata
это Redux Teunk Action Возвращая обещание при отправке.
На сервере мы можем использовать специальную функцию из Реагистрационный маршрутизатор
называется MatchRoute
Отказ
/* ... */ import { StaticRouter, matchPath } from "react-router-dom"; import routes from "./routes"; /* ... */ app.get( "/*", ( req, res ) => { /* ... */ const dataRequirements = routes .filter( route => matchPath( req.url, route ) ) // filter matching paths .map( route => route.component ) // map to components .filter( comp => comp.serverFetch ) // check if components have data requirement .map( comp => store.dispatch( comp.serverFetch( ) ) ); // dispatch data requirement Promise.all( dataRequirements ).then( ( ) => { const jsx = (); const reactDom = renderToString( jsx ); const reduxState = store.getState( ); res.writeHead( 200, { "Content-Type": "text/html" } ); res.end( htmlTemplate( reactDom, reduxState ) ); } ); } ); /* ... */
При этом мы получаем список компонентов, которые будут установлены при реакцию, отображаются на строку на текущем URL-адресе.
Мы собираем Требования к данным И мы ждем всех вызовов API для возвращения. Наконец, возобновим сервер рендеринга, но с данными, уже доступными в Redux.
Полный пример можно увидеть на Fetch-data
Тег в тот же репозиторий Отказ
Вы, вероятно, замечаете, что это поставляется с производительностью наказание, потому что мы закладываем рендеринг, пока данные не будут заработаны.
Это то, где вы начинаете сравнивать метрики и сделать все возможное, чтобы понять, какие звонки необходимы, а не. Например, выбор продуктов для приложения электронного коммерции могут быть решающими, но цены и фильтры боковой панели могут быть ленивы.
Шлем
Как бонус, давайте посмотрим на SEO. Работая с React, вы можете установить разные значения в вашем <он
Ad> Тег. Например, вы можете захотеть посмотреть T the T ITLE, встретил А Теги, ключ слова и так далее.
Имейте в виду, что <он
AD> Тег обычно не является частью вашего приложения React!
Реагистрационный шлем Вы находитесь в этом сценарии. И это имеет большую поддержку для SSR.
import React from "react"; import Helmet from "react-helmet"; const Contact = () => (); export default Contact;This is the contact page
Contact Page
Вы просто добавляете свой голова
данные в любом месте вашего компонента. Это дает вам поддержку изменения значений за пределами установленного приложения RACT на клиенте.
И теперь мы добавляем поддержку SSR:
/* ... */ import Helmet from "react-helmet"; /* ... */ app.get( "/*", ( req, res ) => { /* ... */ const jsx = (); const reactDom = renderToString( jsx ); const reduxState = store.getState( ); const helmetData = Helmet.renderStatic( ); res.writeHead( 200, { "Content-Type": "text/html" } ); res.end( htmlTemplate( reactDom, reduxState, helmetData ) ); } ); } ); app.listen( 2048 ); function htmlTemplate( reactDom, reduxState, helmetData ) { return ` ${ helmetData.title.toString( ) } ${ helmetData.meta.toString( ) } React SSR /* ... */ `; }
И теперь у нас есть полнофункциональный пример реагирования SSR!
Мы начали с простого визуализации HTML в контексте Экспресс приложение. Мы постепенно добавляем маршрутизацию, государственное управление и данные для получения данных. Наконец, мы обрабатываем изменения за пределами объема приложения реагирования.
Окончательная кодовая база на Мастер
на Тот же репозиторий это было упомянуто раньше.
Заключение
Как вы видели, SSR не имеет большого значения, но он может получить комплекс. И намного проще понять, если вы построете свои потребности шаг за шагом.
Стоит ли добавить SSR в ваше приложение? Как всегда, это зависит. Это обязательно, если ваш сайт является публичным и доступным для сотен тысяч пользователей. Но если вы строете инструмент/приборное приложение, это может не стоить усилий.
Однако используя мощность универсальных приложений – это шаг вперед для перспективного сообщества.
Вы используете аналогичный подход для SSR? Или ты думаешь, что что-то пропустил? Оставьте мне сообщение ниже или на Twitter Отказ
Если вы нашли эту статью полезную, помогите мне поделиться с этим сообществом!