【问题标题】:Using renderToNodeStream with react-helmet将 renderToNodeStream 与 react-helmet 一起使用
【发布时间】:2020-06-04 22:27:18
【问题描述】:

我目前正在创建一个网站,并且一直希望开始使用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));
    });
  });
}

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

【问题讨论】:

  • 嘿菲尔,你解决了你面临的这个问题吗?我们正要看看使用renderToNodeStream
  • 这里是my post 了解更多信息和帮助

标签: reactjs react-dom react-helmet


【解决方案1】:

这是我最近也一直在努力解决的一个问题——我一直在尝试将应用程序从 renderToString 迁移到 renderToNodeStream,并且在尝试让动态头部数据正常工作时花了很多时间。

所以,很遗憾react-helmet 没有为 renderToNodeStream 提供开箱即用的支持。我知道有两个库可供您使用。签出:

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

*虽然react-helmet-async 的文档有一个关于如何使用renderToNodeStream 库的快速指南,但作者最近表示它不受官方支持(@see https://github.com/staylor/react-helmet-async/issues/37#issuecomment-573361267

此外,我看到了 Loadable.preloadAll 函数调用 - 您还必须迁移到支持 renderToNodeStream 的 loadable-components

因此,假设您迁移到loadable-components 和上面的头盔库之一,如果您的头部/头盔数据是静态的,我相信一切都应该适合您开箱即用。如果您的头部数据依赖于 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>
});

希望这有助于您走上正确的道路。祝你好运。

【讨论】:

  • 嗨!对于反应中的ssr,我使用react-helmet-asyncloadable-componentreact-router-dommaterial-ui,所以有一些工作表收集器,当然还有express.js。我试过你的代码,它可以工作,但我仍然在页面速度洞察中得到初始响应时间的错误,我的分数变得更糟。我不知道如何做到这一点,所以我可以更快地获得第一个响应。换句话说,我不知道如何获得我以流的方式响应并连续获取小字节以提高我的性能。提前谢谢你@Jon Eric Escobedo
  • 当我看到性能下降时,我转而使用 renderToStaticMarkup。但正如我所说,初始响应时间的得分损失很大。另一件事是我正在重新渲染当它完成 ssr 并启动 csr 时,它会闪烁并创建一个不太棒的用户体验。我很乐意在这里尝试任何解决方案。
  • 这里是my post了解更多信息和帮助
猜你喜欢
  • 1970-01-01
  • 2016-11-20
  • 2018-07-18
  • 2017-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多