使用 react-helmet 的 renderToNodeStream。

问题描述 投票:0回答:1

我目前正在创建一个网站,并一直在寻找开始使用。renderToNodeStream 来提高服务器端渲染的性能,而不是使用 renderToString.

目前我使用的是 renderToString 然后用 Helmet.renderStatic 来获取每个页面所需的所有元数据和标题。当我切换到使用 renderToNodeStream 然而,我将在渲染任何东西之前向头部写入,因此不能再使用 Helmet.renderStatic 了。

我在想我可以做下面的事情来解决这个问题,但这涉及到先用 renderToString 然后再使用 renderToNodeStream,可能并没有真正的改善...

app.use('*', (req, res) {
  Loadable.preloadAll().then(() => {
    const store = createStore(
      reducers,
      getDefaultStateFromProps(),
      applyMiddleware(thunk)
    );
    const routeContext = {};
    const router = (
      <Provider store={store}>
        <StaticRouter location={req.url} context={routeContext}>
          <App/>
        </StaticRouter>
      </Provider>
    );

    res.setHeader('Content-Type', 'text/html');

    renderToString(router);
    const helmet = Helmet.renderStatic();

    res.locals.title = helmet.title;
    res.locals.meta = helmet.meta;
    res.locals.link = helmet.link;

    res.write(headTemplate(res.locals));

    const stream = renderToNodeStream(router);

    stream.pipe(res, { end: false });
    stream.on('end', () => {
      res.locals.context = JSON.stringify(store.getState());
      res.end(bodyTemplate(res.locals));
    });
  });
}

有人知道如何解决这个问题吗?

reactjs react-dom react-helmet
1个回答
1
投票

这也是我最近一直在努力解决的问题--我一直在尝试将一个应用从renderToString迁移到renderToNodeStream,并且在尝试让动态头数据工作的时候遇到了麻烦。

所以,不幸的是 react-helmet 并没有提供开箱即用的 renderToNodeStream 支持。据我所知,有两个库你可以使用。请看一下。

  • react-helmet-async*
  • react-safety-helmet

*虽然文件中提到 react-helmet-async 有一个关于如何使用该库与renderToNodeStream的快速指南,作者最近说还没有得到官方的支持(@见 https:/github.comstaylorreact-helmet-asyncissues37#issuecomment-573361267。)

此外,我看到 Loadable.preloadAll 函数调用--你也必须迁移到到 loadable-components 支持 renderToNodeStream。

所以,假设你迁移到 loadable-components 和上面的一个头盔库,如果你的Head头盔数据是静态的,我相信一切都应该可以为你马上工作。如果你的头盔数据依赖于API调用,你可能需要考虑添加一些类似于 react-ssr-prepass.

我个人最后用的是 react-safety-helmet; 这是我采取的基本方法。

客户端

import { loadableReady } from '@loadable/component';
import { createHelmetStore, HelmetProvider } from 'react-safety-helmet';

const helmetStore = createHelmetStore();

loadableReady(() => {
  const root = document.getElementById('app-root')
  hydrate(
    <HelmetProvider store={helmetStore}
        <Provider store={store}>
            <StaticRouter location={req.url} context={routeContext}>
                <App/>
            </StaticRouter>
        </Provider>
    </HelmetProvider>, root)
})

服务器

import { renderToNodeStream } from 'react-dom/server';
import { ChunkExtractor } from '@loadable/server';
import { createHelmetStore, HelmetProvider } from 'react-safety-helmet';

const statsFile = path.resolve('../dist/loadable-stats.json')

const extractor = new ChunkExtractor({ statsFile })

new Promise((resolve, reject) => {
    const helmetStore = createHelmetStore();
    let body = '';
    const router = (
      <HelmetProvider store={helmetStore}>
            <Provider store={store}>
                <StaticRouter location={req.url} context={routeContext}>
                    <App/>
                </StaticRouter>
            </Provider>
        </HelmetProvider>,
    );
    renderToNodeStream(router)
      .on('data', (chunk) => {
        body += chunk;
      })
      .on('error', (err) => {
        reject(err);
      })
      .on('end', () => {
        resolve({
          body,
          helmet: helmetStore.renderStatic(),
        });
      });
}).then(({body, helmet}) => {
    // Create html with body and helmet object
    const linkTags = extractor.getLinkTags();
    const styleTags = extractor.getStyleTags();

    // This will be dependent on your implementation
    const htmlStates = {
        helmet, // From the resolved promise above
        store: store.getState(),
        linksTags,
        styleTags
    };

    const [startHtml, endHtml] = htmlTemplate(htmlStates); // Will vary on your implementation

    res.write(startHtml);
    res.write(body) // From the resolved promise above
    res.end(`${extractor.getScriptTags()}${endHtml}`) // This will vary as well - just make sure to add your JS tags before the closing </body></html>
});

希望能帮助你走上正确的道路。祝您好运。

© www.soinside.com 2019 - 2024. All rights reserved.