Предполагается, что одним из самых больших преимуществ React.js является рендеринг на стороне сервера . Проблема в том, что ключевая функция React.renderComponentToString()
является синхронной, что делает невозможным загрузку каких-либо асинхронных данных, поскольку иерархия компонентов отображается на сервере.
Допустим, у меня есть универсальный компонент для комментирования, который я могу разместить где угодно на странице. У него есть только одно свойство, какой-то идентификатор (например, id статьи, под которой размещаются комментарии), а все остальное обрабатывается самим компонентом (загрузка, добавление, управление комментариями).
Мне очень нравится архитектура Flux, потому что она значительно упрощает многие вещи, а ее хранилища идеально подходят для обмена состоянием между сервером и клиентом. Как только мой магазин, содержащий комментарии, инициализирован, я могу просто сериализовать его и отправить с сервера клиенту, где его легко восстановить.
Вопрос в том, как лучше всего заполнить мой магазин. В последние дни я много гуглил и наткнулся на несколько стратегий, ни одна из которых не казалась действительно хорошей, учитывая, насколько активно эта функция React «продвигается».
На мой взгляд, самый простой способ - заполнить все мои магазины до начала фактического рендеринга. Это означает, что где-то вне иерархии компонентов (например, подключен к моему маршрутизатору). Проблема с этим подходом в том, что мне пришлось бы дважды определять структуру страницы. Рассмотрим более сложную страницу, например страницу блога с множеством различных компонентов (фактическое сообщение в блоге, комментарии, связанные сообщения, новейшие сообщения, поток Twitter ...). Мне пришлось бы спроектировать структуру страницы с использованием компонентов React, а затем где-то еще мне нужно было бы определить процесс заполнения каждого необходимого хранилища для этой текущей страницы. Мне это не кажется хорошим решением. К сожалению, большинство изоморфных туториалов разработано именно таким образом (например, этот отличный флюкс-туториал ).
React-async, . Такой подход идеален. Это позволяет мне просто определить в специальной функции в каждом компоненте, как инициализировать состояние (независимо от того, синхронно или асинхронно), и эти функции вызываются по мере того, как иерархия отображается в HTML. Он работает таким образом, что компонент не отображается до тех пор, пока состояние не будет полностью инициализировано. Проблема в том, что для этого требуются волокна который, насколько я понимаю, является расширением Node.js, изменяющим стандартное поведение JavaScript. Хотя результат мне очень нравится, мне все же кажется, что вместо того, чтобы найти решение, мы изменили правила игры. И я думаю, что нас не следует заставлять делать это, чтобы использовать эту основную функцию React.js. Я также не уверен в общей поддержке этого решения. Можно ли использовать Fiber на стандартном хостинге Node.js?
Я немного подумал сам. Я особо не продумывал детали реализации, но общая идея заключается в том, что я бы расширил компоненты аналогично React-async, а затем я бы неоднократно вызывал React.renderComponentToString () для корневого компонента. Во время каждого прохода я собирал расширяющиеся обратные вызовы, а затем вызывал их в и на проходе для заполнения магазинов. Я бы повторил этот шаг до тех пор, пока все хранилища, необходимые для текущей иерархии компонентов, не будут заполнены. Есть много вещей, которые нужно решить, и я особенно не уверен в производительности.
Я что-то пропустил? Есть другой подход / решение? Прямо сейчас я думаю о том, чтобы пойти по пути реакции-асинхронности / волокон, но я не совсем уверен в этом, как описано во втором пункте.
Связанное обсуждение на GitHub . Судя по всему, официального подхода или даже решения нет. Возможно, реальный вопрос заключается в том, как будут использоваться компоненты React. Как простой слой представления (в значительной степени мое предложение номер один) или как настоящие независимые и автономные компоненты?
источник
Ответы:
Если вы используете response-router , вы можете просто определить
willTransitionTo
методы в компонентах, которым передаетсяTransition
объект, который вы можете вызвать.wait
.Не имеет значения, является ли renderToString синхронным, потому что обратный вызов
Router.run
не будет вызываться до тех пор, пока все.wait
ed обещания не будут разрешены, поэтому к моменту вызоваrenderToString
в промежуточном программном обеспечении вы могли бы заполнить хранилища. Даже если хранилища являются одиночными, вы можете просто временно установить их данные точно в срок до вызова синхронного рендеринга, и компонент их увидит.Пример промежуточного программного обеспечения:
В
routes
Объект (который описывает иерархию маршрутов) делится дословно с клиентом и серверомисточник
willTransitionTo
метод поддерживают только компоненты маршрута . Это означает, что по-прежнему невозможно написать полностью автономные повторно используемые компоненты, подобные тому, который я описал в вопросе. Но если мы не готовы идти с волокнам, это, вероятно , самый лучший и самый реагировать способ реализовать серверный рендеринг.transition
объект в качестве параметра, поэтому вы просто вызоветеtransition.wait(yourPromise)
. Это, конечно, означает, что вы должны реализовать свой API для поддержки обещаний. Еще один недостаток этого подхода состоит в том, что нет простого способа реализовать «индикатор загрузки» на стороне клиента. Переход не переключится на компонент обработчика маршрута, пока не будут выполнены все обещания..waited
для перехода. Как только все они выполнены,.run
вызывается обратный вызов. Непосредственно перед тем, как.render()
я собираю все данные из обещаний и устанавливаю состояния хранилища синглтонов, на следующей строке после вызова рендеринга я снова инициализирую хранилища синглтонов. Это довольно хитроумно, но все происходит автоматически, а код компонента и приложения магазина остается практически неизменным.Я знаю, что это, вероятно, не совсем то, что вы хотите, и это может не иметь смысла, но я помню, как обходился небольшим изменением компонента для обработки обоих:
Так что-то вроде:
Извините, у меня нет точного кода под рукой, так что это может не сработать из коробки, но я публикую в интересах обсуждения.
Опять же, идея состоит в том, чтобы рассматривать большую часть компонента как глупое представление и иметь дело с извлечением как можно большего количества данных из компонента.
источник
<script type=application/json>{initState}</script>
; таким образом данные будут в HTML. Повторная гидратация / привязка событий пользовательского интерфейса к странице путем вызова render на клиенте. Последующие страницы создаются клиентским js-кодом (при необходимости извлекают данные) и обрабатываются клиентом. Таким образом, при любом обновлении будут загружены свежие страницы SSR, а нажатие на страницу будет CSR. = изоморфный и оптимизированный для SEOСегодня я действительно напортачил с этим, и хотя это не ответ на вашу проблему, я использовал этот подход. Я хотел использовать Express для маршрутизации, а не React Router, и я не хотел использовать Fibers, поскольку мне не требовалась поддержка потоковой передачи в node.
Поэтому я только что принял решение, что для начальных данных, которые необходимо отобразить в хранилище потоков при загрузке, я выполню запрос AJAX и передам исходные данные в хранилище.
В этом примере я использовал Fluxxor.
Итак, на моем экспресс-маршруте, в данном случае это
/products
маршрут:Затем я инициализирую метод рендеринга, который передает данные в магазин.
источник
Я знаю, что этот вопрос был задан год назад, но у нас была такая же проблема, и мы решаем ее с помощью вложенных обещаний, которые были получены из компонентов, которые будут отображаться. В конце концов, у нас были все данные для приложения, и мы просто отправили их.
Например:
и в роутере
источник
Хочу поделиться с вами своим подходом к рендерингу на стороне сервера
Flux
, можно немного упростить, например:Допустим, у нас есть
component
исходные данные из магазина:Если классу требуются некоторые предварительно загруженные данные для начального состояния, давайте создадим загрузчик для
MyComponent
:Хранить:
Теперь просто загрузите данные в роутер:
источник
так же, как короткий набор -> GraphQL решит эту проблему полностью для вашего стека ...
-> getDataFromTree автоматически найдет все задействованные запросы в вашем приложении и выполнит их, поместив ваш кеш apollo на сервер и, таким образом, включив полностью рабочий SSR.
источник