Вышел React 16! Рассказывая об этом событии, можно упомянуть множество замечательных новостей (вроде архитектуры ядра Fibers), но лично меня больше всего восхищают улучшения серверного рендеринга. Предлагаю подробно всё это разобрать и сравнить с тем, что было раньше. Надеюсь, серверный рендеринг в React 16 понравится вам так же, как он понравился мне.



Как работает SSR в React 15


Для начала вспомним, как серверный рендеринг (Server-Side Rendering, SSR) выглядит в React 15. Для выполнения SSR обычно поддерживают сервер, основанный на Node, использующий Express, Hapi или Koa, и вызывают renderToString для преобразования корневого компонента в строку, которую затем записывают в ответ сервера:

// используем Express
import { renderToString } from "react-dom/server"
import MyPage from "./MyPage"
app.get("/", (req, res) => {
  res.write("<!DOCTYPE html><html><head><title>My Page</title></head><body>");
  res.write("<div id='content'>");  
  res.write(renderToString(<MyPage/>));
  res.write("</div></body></html>");
  res.end();
});

Когда клиент получает ответ, клиентской подсистеме рендеринга, в коде шаблона, отдают команду восстановить HTML, сгенерированный на сервере, используя метод render(). Тот же метод используют и в приложениях, выполняющих рендеринг на клиенте без участия сервера:

import { render } from "react-dom"
import MyPage from "./MyPage"
render(<MyPage/>, document.getElementById("content"));

Если сделать всё правильно, клиентская система рендеринга может просто использовать HTML, сгенерированный на сервере, не обновляя DOM.

Как же SSR выглядит в React 16?

Обратная совместимость React 16


Команда разработчиков React показала чёткую ориентацию на обратную совместимость. Поэтому, если ваш код выполняется в React 15 без сообщений о применении устаревших конструкций, он должен просто работать в React 16 без дополнительных усилий с вашей стороны. Код, приведённый выше, например, нормально работает и в React 15, и в React 16.

Если случится так, что вы запустите своё приложение на React 16 и столкнётесь с ошибками, пожалуйста, сообщите о них! Это поможет команде разработчиков.

Метод render() становится методом hydrate()


Надо отметить, что переходя с React 15 на React 16, вы, возможно, столкнётесь со следующим предупреждением в браузере.


Очередное полезное предупреждение React. Метод render() теперь называется hydrate()

Оказывается, в React 16 теперь есть два разных метода для рендеринга на клиентской стороне. Метод render() для ситуаций, когда рендеринг выполняются полностью на клиенте, и метод hydrate() для случаев, когда рендеринг на клиенте основан на результатах серверного рендеринга. Благодаря обратной совместимости новой версии React, render() будет работать и в том случае, если ему передать то, что пришло с сервера. Однако, эти вызовы следует заменить вызовами hydrate() для того, чтобы система перестала выдавать предупреждения, и для того, чтобы подготовить код к React 17. При таком подходе код, показанный выше, изменился бы так:

import { hydrate } from "react-dom"
import MyPage from "./MyPage"
hydrate(<MyPage/>, document.getElementById("content"))


React 16 может работать с массивами, строками и числами


В React 15 метод компонента render() должен всегда возвращать единственный элемент React. Однако, в React 16 рендеринг на стороне клиента позволяет компонентам, кроме того, возвращать из метода render() строку, число, или массив элементов. Естественно, это касается и SSR.

Итак, теперь можно выполнять серверный рендеринг компонентов, который выглядит примерно так:

class MyArrayComponent extends React.Component {
  render() {
    return [
      <div key="1">first element</div>, 
      <div key="2">second element</div>
    ];
  }
}
class MyStringComponent extends React.Component {
  render() {
    return "hey there";
  }
}
class MyNumberComponent extends React.Component {
  render() {
    return 2;
  }
}

Можно даже передать строку, число или массив компонентов методу API верхнего уровня renderToString:

res.write(renderToString([
      <div key="1">first element</div>, 
      <div key="2">second element</div>
    ]));
// Не вполне ясно, зачем так делать, но это работает!
res.write(renderToString("hey there"));
res.write(renderToString(2));

Это должно позволить вам избавиться от любых div и span, которые просто добавлялись к вашему дереву компонентов React, что ведёт к общему уменьшению размеров HTML-документов.

React 16 генерирует более эффективный HTML


Если говорить об уменьшении размеров HTML-документов, то React 16, кроме того, радикально снижает излишнюю нагрузку, создаваемую SSR при формировании HTML-кода. В React 15 каждый HTML-элемент в SSR-документе имеет атрибут data-reactid, значение которого представляет собой монотонно возрастающие ID, и текстовые узлы иногда окружены комментариями с react-text и ID. Для того, чтобы это увидеть, рассмотрим следующий фрагмент кода:

renderToString(
  <div>
    This is some <span>server-generated</span> <span>HTML.</span>
  </div>
);

В React 15 этот фрагмент сгенерирует HTML-код, который выглядит так, как показано ниже (переводы строк добавлены для улучшения читаемости кода):

<div data-reactroot="" data-reactid="1" 
    data-react-checksum="122239856">
  <!-- react-text: 2 -->This is some <!-- /react-text -->
  <span data-reactid="3">server-generated</span>
  <!-- react-text: 4--> <!-- /react-text -->
  <span data-reactid="5">HTML.</span>
</div>

В React 16, однако, все ID удалены из разметки, в результате HTML, полученный из такого же фрагмента кода, окажется значительно проще:

<div data-reactroot="">
  This is some <span>server-generated</span> <span>HTML.</span>
</div>

Такой подход, помимо улучшения читаемости кода, может значительно уменьшить размер HTML-документов. Это просто здорово!

React 16 поддерживает произвольные атрибуты DOM


В React 15 система рендеринга DOM была довольно сильно ограничена в плане атрибутов HTML-элементов. Она убирала нестандартные HTML-атрибуты. В React 16, однако, и клиентская, и серверная системы рендеринга теперь пропускают произвольные атрибуты, добавленные к HTML-элементам. Для того, чтобы узнать больше об этом новшестве, почитайте пост Дэна Абрамова в блоге React.

SSR в React 16 не поддерживает обработчики ошибок и порталы


В клиентской системе рендеринга React есть две новых возможности, которые, к сожалению, не поддерживаются в SSR. Это — обработчики ошибок (Error Boundaries) и порталы (Portals). Обработчикам ошибок посвящён отличный пост Дэна Абрамова в блоге React. Учитывайте однако, что (по крайней мере сейчас) обработчики не реагируют на серверные ошибки. Для порталов, насколько я знаю, пока даже нет пояснительной статьи, но Portal API требует наличия узла DOM, в результате, на сервере его использовать не удастся.

React 16 производит менее строгую проверку на стороне клиента


Когда вы восстанавливаете разметку на клиентской стороне в React 15, вызов ReactDom.render() выполняет посимвольное сравнение с серверной разметкой. Если по какой-либо причине будет обнаружено несовпадение, React выдаёт предупреждение в режиме разработки и заменяет всё дерево разметки, сгенерированной на сервере, на HTML, который был сгенерирован на клиенте.

В React 16, однако, клиентская система рендеринга использует другой алгоритм для проверки правильности разметки, которая пришла с сервера. Эта система, в сравнении с React 15, отличается большей гибкостью. Например, она не требует, чтобы разметка, созданная на сервере, содержала атрибуты в том же порядке, в котором они были бы расположены на клиентской стороне. И когда клиентская система рендеринга в React 16 обнаруживает расхождения, она лишь пытается изменить отличающееся поддерево HTML, вместо всего дерева HTML.

В целом, это изменение не должно особенно сильно повлиять на конечных пользователей, за исключением одного факта: React 16, при вызове ReactDom.render() / hydrate(), не исправляет несовпадающие HTML-атрибуты, сгенерированные SSR. Эта оптимизация производительности означает, что вам понадобится внимательнее относиться к исправлению несовпадений разметки, приводящих к предупреждениям, которые вы видите в режиме development.

React 16 не нужно компилировать для улучшения производительности


В React 15, если вы используете SSR в таком виде, в каком он оказывается сразу после установки, производительность оказывается далеко не оптимальной, даже в режиме production. Это так из-за того, что в React есть множество замечательных предупреждений и подсказок для разработчика. Каждое из этих предупреждений выглядит примерно так:

if (process.env.NODE_ENV !== "production") {
  // что-то тут проверить и выдать полезное
  // предупреждение для разработчика.
}

К сожалению, оказывается, что process.env — это не обычный объект JavaScript, и обращение к нему — операция затратная. В итоге, даже если значение NODE_ENV установлено в production, частая проверка переменной окружения ощутимо замедляет серверный рендеринг.

Для того, чтобы решить эту проблему в React 15, нужно было бы скомпилировать SSR-код для удаления ссылок на process.env, используя что-то вроде Environment Plugin в Webpack, или плагин transform-inline-environment-variables для Babel. По опыту знаю, что многие не компилируют свой серверный код, что, в результате, значительно ухудшает производительность SSR.

В React 16 эта проблема решена. Тут имеется лишь один вызов для проверки process.env.NODE_ENV в самом начале кода React 16, в итоге компилировать SSR-код для улучшения производительности больше не нужно. Сразу после установки, без дополнительных манипуляций, мы получаем отличную производительность.

React 16 отличается более высокой производительностью


Если продолжить разговор о производительности, можно сказать, что те, кто использовал серверный рендеринг React в продакшне, часто жаловались на то, что большие документы обрабатываются медленно, даже с применением всех рекомендаций по улучшению производительности. Тут хочется отметить, что рекомендуется всегда проверять, чтобы переменная NODE_ENV была установлена в значение production, когда вы используете SSR в продакшне.

С удовольствием сообщают, что, проведя кое-какие предварительные тесты, я обнаружил значительное увеличение производительности серверного рендеринга React 16 на различных версиях Node:


Рендеринг на сервере в React 16 быстрее, чем в React 15. Чем ниже столбик — тем результат лучше

При сравнении с React 16, даже с учётом того, что в React 15 обращения к process.env были устранены благодаря компиляции, наблюдается рост производительности примерно в 2.4 раза в Node 4, в 3 раза — в Node 6, и замечательный рост в 3.8 раза в Node 8.4. Если сравнить React 16 и React 15 без компиляции последнего, результаты на последней версии Node будут просто потрясающими.

Почему React 16 настолько быстрее, чем React 15? Итак, в React 15 серверные и клиентские подсистемы рендеринга представляли собой, в общих чертах, один и тот же код. Это означает потребность в поддержке виртуального DOM во время серверного рендеринга, даже учитывая то, что этот vDOM отбрасывался как только осуществлялся возврат из вызова renderToString. В результате, на сервере проводилось много ненужной работы.

В React 16, однако, команда разработчиков переписала серверный рендеринг с нуля, и теперь он совершенно не зависит от vDOM. Это и даёт значительный рост производительности.

Тут хотелось бы сделать одно предупреждение, касающееся ожидаемого роста производительности реальных проектов после перехода на React 16. Проведённые мной тесты заключались в создании огромного дерева из <span> с одним очень простым рекурсивным компонентом React. Это означает, что мой бенчмарк относится к разряду синтетических и почти наверняка не отражает сценарии реального использования React. Если в ваших компонентах имеется множество сложных методов render, обработка которых занимает много циклов процессора, React 16 ничего не сможет сделать для того, чтобы их ускорить. Поэтому, хотя я и ожидаю увидеть ускорение серверного рендеринга при переходе на React 16, я не жду, скажем, трёхкратного роста производительности в реальных приложениях. По непроверенным данным, при использовании React 16 в реальном проекте, удалось достичь роста производительности примерно в 1.3 раза. Лучший способ понять, как React 16 отразится на производительности вашего приложения — попробовать его самостоятельно.

React 16 поддерживает потоковую передачу данных


Последняя из новых возможностей React, о которой хочу рассказать, не менее интересна, чем остальные. Это — рендеринг непосредственно в потоки Node.

Потоковый рендеринг может уменьшить время до получения первого байта (TTFB, Time To First Byte). Начало документа попадает в браузер ещё до создания продолжения документа. В результате, все ведущие браузеры быстрее приступят к разбору и рендерингу документа.
Ещё одна отличная вещь, которую может получить от рендеринга в поток — это возможность реагировать на ситуацию, когда сервер выдаёт данные быстрее, чем сеть может их принять. На практике это означает, что если сеть перегружена и не может принимать данные, система рендеринга получит соответствующий сигнал и приостановит обработку данных до тех пор, пока нагрузка на сеть не упадёт. В результате окажется, что сервер будет использовать меньше памяти и сможет быстрее реагировать на события ввода-вывода. И то и другое способно помочь серверу нормально работать в сложных условиях.

Для того, чтобы организовать потоковый рендеринг, нужно вызвать один из двух новых методов react-dom/server: renderToNodeStream или renderToStaticNodeStream, которые соответствуют методам renderToString и renderToStaticMarkup. Вместо возврата строки эти методы возвращают объект Readable. Такие объекты используются в модели работы с потоками Node для сущностей, генерирующих данные.

Когда вы получаете поток Readable из методов renderToNodeStream или renderToStaticNodeStream, он находится в режиме приостановки, то есть, рендеринг в этот момент ещё не начинался. Рендеринг начнётся только в том случае, если вызвать read, или, более вероятно, подключить поток Readable с помощью pipe к потоку Writable. Большинство веб-фреймворков Node имеют объект ответа, который унаследован от Writable, поэтому обычно можно просто перенаправить Readable в ответ.

Скажем, вышеприведённый пример с Express можно было бы переписать для потокового рендеринга следующим образом:

// используем Express
import { renderToNodeStream } from "react-dom/server"
import MyPage from "./MyPage"
app.get("/", (req, res) => {
  res.write("<!DOCTYPE html><html><head><title>My Page</title></head><body>");
  res.write("<div id='content'>"); 
  const stream = renderToNodeStream(<MyPage/>);
  stream.pipe(res, { end: false });
  stream.on('end', () => {
    res.write("</div></body></html>");
    res.end();
  });
});

Обратите внимание на то, что когда мы перенаправляем поток в объект ответа, нам необходимо использовать необязательный аргумент { end: false } для того, чтобы сообщить потоку о том, что он не должен автоматически завершать ответ при завершении рендеринга. Это позволяет нам закончить оформление тела HTML-документа, и, как только поток будет полностью записан в ответ, завершить ответ самостоятельно.

Подводные камни потокового рендеринга


Потоковый рендеринг способен улучшить многие сценарии SSR, однако, существуют некоторые шаблоны, которым потоковая передача данных на пользу не пойдёт.

В целом, любой шаблон, в котором на основе разметки, созданной в ходе серверного рендеринга, формируются данные, которые надо добавить в документ до этой разметки, окажется фундаментально несовместимым с потоковой передачей данных. Среди примеров подобного — фреймворки, которые динамически определяют, какие CSS-правила надо добавить на страницу в предшествующем сгенерированной разметке теге <style>, или фреймворки, которые добавляют элементы в тег <head> документа в процессе рендеринга тела документа. Если вы используете подобные фреймворки, вам, вероятно, придётся применять обычный рендеринг.

Ещё один шаблон, который ещё не работает в React 16 — это встроенные вызовы renderToNodeStream в деревьях компонента. Обычное дело в React 15 — использовать renderToStaticMarkup для создания шаблона страницы и встраивать вызовы renderToString для формирования динамического содержимого. Например, это может выглядеть так:

res.write("<!DOCTYPE html>");
res.write(renderToStaticMarkup(
  <html>
    <head>
      <title>My Page</title>
    </head>
    <body>
      <div id="content">
        { renderToString(<MyPage/>) }
      </div>
    </body>
  </html>);

Однако, если заменить эти вызовы подсистемы рендеринга на их потоковые аналоги, код перестанет работать. Потки Readable (которые возвращаются из renderToNodeStream) пока невозможно встраивать в компоненты как элементы. Надеюсь, такая возможность ещё будет добавлена в React.

Итоги


Итак, выше мы рассмотрели основные новшества серверного рендеринга в React 16. Надеюсь, вам они понравились так же, как и мне. В заключение хочу сказать огромное спасибо всем, кто участвовал в разработке React 16.

Продолжаете читать? Вообще-то, пора бы уже с этим завязывать и попробовать что-нибудь отрендерить.

Уважаемые читатели! Вы ещё здесь? Похоже, серверный рендеринг в React 16 вы уже испытали. Если так — просим поделиться впечатлениями.

Комментарии (18)


  1. KasperGreen
    02.10.2017 20:44

    Метод hydrate учитывает, что данные для рендера могут подгружаться асинхронно? С этим будет из коробки работать Redux?


    1. Nerlin
      03.10.2017 09:02

      Эту логику вы должны реализовать на сервере, прежде чем вызывать метод renderToString или renderToNodeStream — загружаете нужные данные согласно текущему url, формируете из них store и уже все это готовое отдаете в компонент Provider, который затем рендерите через renderToString или renderToNodeStream.


      1. KasperGreen
        03.10.2017 11:19
        -2

        Понятно. prerender.io по прежнему в фаворитах. Он готовит страницу как браузер и может передавать её nginx прокси например для кеширования.


        Это избавляет от жонглирования датастором и решает проблемы индексации. Google кстати в нём не нуждается и сам исполняет JS о чём prerender.io знает и ему лишний трафик не отдаёт.


        Иначе двойной трафик получается. Вёрстку загрузи с датастором, потом бандл загрузи, датастор снова загрузи. И всё это время вместо favicon — кругляш загрузки.


        Конечно время до показа первого экрана важная метрика, но велика плата за неё в виде дополнительного трафика который по сути не нужен клиенту.


        1. iNikNik
          03.10.2017 12:16
          -1

          Зачем двойной трафик? Если вы используете редакс — нужно загрузить все данные на сервере, потом отправить заполненный стор на клиент (как часть HTML-страницы). Затем, на клиенте, можно легко проверить, что данные уже есть в сторе и ничего не загружать


          1. KasperGreen
            03.10.2017 12:53
            -1

            Ок. Из api можно и правда дважды не грузить. Дважды загрузится только вёрстка. Вернее результат и бандл по которому он построен. Возможно это совсем крохотная разница, а излишки вёрстки в виде датастора в конце страницы можно удалять после его восстановления в redux. А может так статься, что вёрстка превысит размеры бандла, тогда это всё будет иметь смысл только на крайне медленных девайсах, на которых время JS рендера страницы критично.


            Моя позиция связана скорее с той болью которая возникает когда приходится постоянно перезагружать дев сервер из-за застрявшего в серверном рендере чего-то и попутно следить, чтобы на сервере загрузились все данные из десятка методов API, а потом решать экзотические проблемы возникающие из-за того, что у тебя серверный рендер вообще есть, тем более дебажится он не так просто (я знаю про --inspect, но не панацея).


            В общем мы были рады переходу на prerender.io, вдоволь налюбившись с рендером на сервере.


            Идея рендера на сервере мне импонирует, но мне кажется она далека от своего завершения. Вместо того чтобы плеваться целиком страницей, стоит выделять важные части за которыми пришёл пользователь.


            Я думаю врятли ему интересен в первую очередь хедер и подвал, а пришёл он скорее за конкретным контентом, будь то список товаров или статья. Так показав ему основной контент можно дальше загружать всё остальное. На это время пользователь занят поглощением контента за которым пришёл.


            Вопросом магии и ловкости рук остаётся положение контента на странице, чтобы он не скаканул и не изменился после загрузки. А также особое меню для тех у кого отключен JS. Но это совсем другая история.


            Зато мы получаем настоящую доступность и сокращение трафика даже для тех девайсов которые будут читать HTML в чистом виде. Ведь ради доступности всё, да?


                                            Всем любви


            1. dagen
              03.10.2017 18:42

              Простите, но prepender.io — это просто генератор статики. Если у вас простой кейз в виде статичных страниц, то может вам и не нужен серверный рендеринг? Вы сами можете в CI или руками генерить когда захотите и что захотите.

              P.S. разметка заранее сгенерированной странички занимает столько же места, сколько разметка, выплюнутая серверным рендером. Подскажите пожалуйста, где здесь дополнительный трафик в случае SSR?


              1. KasperGreen
                03.10.2017 18:57

                ? SSR включен

                index.html 1Mb
                +bundle.js 1Mb


                ? CSR (SSR выключен)

                index.html 10 kb
                +bundle.js 1 Mb


                Prerender.io следит за клиентом, и если рендер ему не нужен отдаёт только коротенький index.html и несколько js/css файлов для самостоятельного рендера на клиенте.


                Возможно я чего то не понимаю и у вас есть LiveView на которых от рендера на сервере количество трафика не увеличилось, а скорость загрузки и работы сайта возросла.


                Я бы посмотрел


                1. dagen
                  03.10.2017 19:04

                  У нас сейчас два параллельно развивающихся проекта на SSR, и в обоих рендерится только то, что нужно (включая критический css). Статическая генерация (если бы она нам подошла, но не подходит из-за высокой динамики контента) никак не помогла бы выиграть в объёме.

                  Расскажите, в чём причина разных объёмов для index.html в вашем случае?


                  1. KasperGreen
                    03.10.2017 20:56

                    В моём случае серверный рендер прибавляет к весу страницы вёрстку. Эти +400кб прилетели конечно почти мгновенно, но похоже было больше на бутафорию.


                    Не всё жмётся (кликается) и не всё отображено так как должно быть (некоторые штуки хотят window. Так сложилось).


                    А потом прилетели 512кб ДжаваСкрипта и всё встало на свои места, но осадочек остался.


                    Я понимаю это мой частный случай и в ином пришлось бы ждать нескольких мегабайт скриптов, но это ещё более оттянуло бы момент нормальной работы кнопок.


                    И вот вся эта бутафория стоит батхерта синхронизации стора? Мне кажется штука крутая конечно, но требует доработки. В первую очередь Promise Midleware, а во вторых акцента на <main></main> / <article></article> секциях.


                    Нужно быть готовым к тому, что исходник будут читать читалки, боты или программисты c собакой, которые знают где ты живёшь, в терминале через cat.


                    1. dagen
                      04.10.2017 13:59
                      +1

                      Не всё жмётся (кликается) и не всё отображено так как должно быть (некоторые штуки хотят window. Так сложилось).
                      То есть у вас неконсистентность рендеринга между клиентом и сервером из-за неправильной реализации или технического долга.

                      И вот вся эта бутафория стоит батхерта синхронизации стора? Мне кажется штука крутая конечно, но требует доработки. В первую очередь Promise Midleware, а во вторых акцента на
                      main / article секциях.
                      Не очень понял, что вы имеете ввиду про main/article. А про доработку: вам наверно подойдёт redux-saga вместе с redux-wait-for-action.

                      И я по-прежнему не понял, почему у вас отличается размер, и что же такого добавляет серверный рендер по сравнению со статической генерацией. Зачем отправлять лишнее, если это не нужно. Возможно проблема опять же из-за неправильной реализации? Ну тогда нет никакого смысла грешить на другие библиотеки.


                      1. KasperGreen
                        04.10.2017 14:24

                        Вы всё правильно не поняли. Разницы нет в размере между статической генерацией и рендером на сервере. Разница с генерацией разметки на клиенте.


                        redux-wait-for-action

                        thx


        1. Nerlin
          03.10.2017 20:43

          В случае наличия сзади сервера на Node.JS, Вы можете отдавать favicon и критический css сразу, а остальное подгружать постепенно через метод renderToNodeStream. Никакого двойного трафика и двойных походов к API не нужно, initial state для Вашего store Вы можете загрузить на сервере, отрендерить контент, а затем сохранить store внутри какого-нибудь window.__initialState__, после чего уже забрать это состояние на клиенте.


          1. KasperGreen
            03.10.2017 21:10

            Погорячился с двойными походами на API. Но вёрстку таки дважды загрузить придётся (+1 раз в качестве внутренностей bundle.js).


            А favicon какой не отдавай, все равно такой будет пока бандлы грузятся.


            window.initialState мы конечно сохраняли. И данные на сервере загружали. Но это сопряжено с временными затратами. После ещё одного потерянного дня на отлов багов серверного рендера, которые сложнее дебажить, мы перешли на prerender.io и довольны.


            Возможно мы опять вернёмся к рендеру на сервере, но когда прогресс пройдётся кремниевыми сапогами по этому раздолью для деятельности. И он идёт, прогресс этот. Шаг ему на встречу React 16 и эта статья. Я верю в то, что сейчас является примером рутины через некоторое время сократится до единого флага в настройках.


            1. dagen
              04.10.2017 14:01

              А почему вам не подошёл изначально любой генератор статики?


    1. matshch
      03.10.2017 12:16

      Мне нравится, как это реализовано в ASP.NET Core: все фетчи, от которых зависит рендер страницы, добавляются с помощью addTask в общий список, и, по завершении выполнения всех записанных задач выполняется повторный рендеринг в строку и сохранение стора (и других сторонних переменных, если нужно).
      Правда, чтобы работали методы, требующие авторизации, необходимо пробрасывать куки из запроса в фетч, использующийся при серверрендеринге, но зато благодаря этому полностью переиспользуется клиентский код, не надо думать о том, что надо запихнуть в стор.


      1. RomanPokrovskij
        04.10.2017 23:04

        Хотелось бы ссылку на статью описывающую эту функциональность ASP Core. Может знаете потолковей? Так на лету я не понял о чем речь, а о Core хочется знать поболее. Фетч это видимо Fetch api, но далее уже не понятно: «от которых зависит рендерер »… Фетч в броузере, рендерер Core (razor?) сервер сайд, что общего? И как может не зависить рендерер от фетчей (кто и как определяет)?


        1. matshch
          04.10.2017 23:26

          К сожалению, ничего толковее https://github.com/aspnet/JavaScriptServices/blob/dev/README.md и шаблонного примера не знаю. Хоть самому писать :)


          Имеются ввиду фетчи, загружающие данные для отрисовки страницы. Если просто использовать renderToString, то везде зарендерится, что данные загружаются, и всё. Обычно, чтобы такого не было, в стор принудительно заливают нужные данные. SpaServices же предлагает все такие фетчи в клиентском коде сохранить в отдельный список с помощью функции addTask, и, во время сервер-рендеринга, по выполнении всех этих фетчей renderToString выполняется повторно. Благодаря тому, что стор уже заполнен фетчами, второй вызов рендера уже генерирует страницу с данными. При этом, фактически, никакого специального серверного кода для подстановки данных писать не надо, просто надо не забывать кидать в общий список нужные фетчи.


          Более того, с помощью пары обходных манёвров, таким же образом можно рендерить и данные, требующие авторизации (этого стандартный шаблон, к сожалению, не показывает).


  1. makamekm
    03.10.2017 17:12

    Tested! All works well! Thanks.