Реагируйте на два прохода рендеринга SSR с помощью renderToString и renderToNodeStream - PullRequest
0 голосов
/ 02 января 2019

Я пытаюсь сделать SSR с ReactDOMServer.renderToNodeStream(element), но просто хотел знать, возникнут ли какие-либо проблемы с использованием ReactDOMServer.renderToString(element) и ReactDOMServer.renderToNodeStream(element) при каждом запросе?

Что у меня есть в моемПользовательская настройка SSR: * React 16 * реагирующая загрузка * styleled-компоненты v4 * реактивный-шлем-асинхронный * Redux * Express JS

Ранее с React я мог легко визуализировать HTML-документ, сначала отобразив <head></head> тегов, которые содержат разметку, создаваемую react-helmet, а затем используют ReactDOMServer.renderToString() для рендеринга элементов React.

Однако при переключении на ReactDOMServer.renderToNodeStream() мне пришлось переключить react-helmet для react-helmet-async, чтоподдерживает функцию renderToNodeStream().Но затем, когда я пытаюсь визуализировать теги <head></head> с разметкой с помощью react-helmet-async, он возвращается как undefined.

Чтобы обойти эту проблему, мне пришлось использовать renderToString()во-первых, не записывая это в Express JS response.Таким образом, react-helmet-async может увидеть, какие метатеги отображать, а затем перейти к использованию renderToNodeStream и передать его в response.

Я максимально упростил свой код, насколько я хочу понять, будет ли это иметь негативное влияние (для производительности или если кто-нибудь может придумать что-нибудь еще)?

До этого:

let html = ReactDOMServer.renderToString(stylesheet.collectStyles(
    <Loadable.Capture report={moduleName => modules.push(moduleName)}>
        <LocalStoreProvider store={store}>
            <HelmetProvider context={helmetContext}>
                <RouterContext {...renderProps} />
            </HelmetProvider>
        </LocalStoreProvider>
    </Loadable.Capture>
));

const { helmet } = helmetContext; 

response.write(
    renderDocumentHead({
        css: stylesheet.getStyleTags(),
        title: helmet.title.toString(),
        link: helmet.link.toString(),
        meta: helmet.meta.toString()
    })
);

response.write(html);

После:

let html = stylesheet.collectStyles(
    <Loadable.Capture report={moduleName => modules.push(moduleName)}>
        <LocalStoreProvider store={store}>
            <HelmetProvider context={helmetContext}>
                <RouterContext {...renderProps} />
            </HelmetProvider>
        </LocalStoreProvider>
    </Loadable.Capture>
);

// do a first pass render so that react-helmet-async 
// can see what meta tags to render
ReactDOMServer.renderToString(html);

const { helmet } = helmetContext; 

response.write(
    renderDocumentHead({
        css: stylesheet.getStyleTags(),
        title: helmet.title.toString(),
        link: helmet.link.toString(),
        meta: helmet.meta.toString()
    })
);

const stream = stylesheet.interleaveWithNodeStream(
    ReactDOMServer.renderToNodeStream(html)
);

// and then actually stream the react elements out
stream.pipe(response, { end: false });

stream.on('end', () => response.end('</body></html>'));

К сожалению, единственный способ заставить react-helmet-async работать правильно, я должен сделать этот двухпроходный рендер.Мои стили CSS и т. Д. Корректно разрешаются, и клиент также корректно отображает / увлажняет.Я видел другие примеры, где использовался react-apollo и использовался метод регидратации данных getDataFromTree, который позволяет react-helmet-async видеть, что необходимо для визуализации разметки головы.Но, надеюсь, нет проблем с моим подходом двухпроходного рендеринга?

...