reactjs - 将 renderToNodeStream 与 react-helmet 一起使用
问题描述
我目前正在创建一个网站,并且一直在寻求开始使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));
});
});
}
有谁知道如何解决这个问题?
解决方案
这是我最近也一直在努力解决的一个问题——我一直在尝试将应用程序从 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
函数调用——你还必须迁移到loadable-components
支持 renderToNodeStream 的地方。
因此,假设您迁移到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>
});
希望这有助于您走上正确的道路。祝你好运。
推荐阅读
- python-3.x - 在 Glumpy Python 3.6 Win 10 中找不到 Freetype 库
- android - 无法在 Xamarin.Forms 中使用 LinearLayout
- postgresql - 使用 HLL 在 Tableau 上执行提取以获取不同计数
- android - 在 Firebase 的列表视图中显示数据 - IndexOutOfBoundsException
- ios - Watch Complication 导致 dyld 崩溃:找不到符号:_OBJC_CLASS_$_CLKFullColorImageProvider
- python - 在python中解析xml文件-找不到元素
- c++ - C++ 中的 MSD 基数排序(字典顺序)
- c# - 从列表中获取数据
柱子 - wordpress - 如何以编程方式在 ACF 中添加自定义字段?
- cmake - 在 CMakeCache.txt 中指定 INCLUDE_PATH 时如何告诉 CMake 递归搜索包含目录