如何让react-router v4的staticContext在loadable-components异步路由中生效?
我之前在维护React SSR项目时也碰到过一模一样的问题——用静态路由时staticContext正常工作,换成loadable-components异步加载路由组件后,staticContext就没反应了。结合你给出的环境版本(react:16.2.0、react-router:4.2.2、node:9.5.0),我来分享下排查思路和解决方案:
核心原因
loadable-components在SSR场景下是异步加载组件的,而react-router的staticContext是在路由匹配的同步过程中传递给组件的。如果异步组件还没加载完成,路由匹配到的其实是loadable的占位组件,无法正确接收和传递staticContext,导致后续服务端无法读取到状态码(比如404)。
具体解决方案
1. 确保异步组件正确接收staticContext
不管是NotFound页面还是其他异步路由组件,都要明确从props中接收staticContext并使用。比如你的NotFound组件应该这样写:
const NotFound = ({ staticContext }) => { // 注意:只有SSR时staticContext才存在,客户端渲染时为undefined if (staticContext) { staticContext.statusCode = 404; } return <div>404 - Page Not Found</div>; }; export default NotFound;
如果用loadable包裹这个组件,要确保loadable会把props(包括staticContext)传递给内部组件——默认情况下loadable会传递props,但如果自定义了render选项,要手动处理:
import loadable from '@loadable/component'; const LoadableNotFound = loadable(() => import('./NotFound'), { // 如果自定义render,务必传递所有props render(loaded, props) { const Component = loaded.default; return <Component {...props} />; }, ssr: true, // 关键:开启SSR模式,确保服务端会加载该组件 });
2. 服务端渲染时等待异步组件加载完成
在你的server.js中,必须等待所有loadable的异步组件加载完成后再渲染路由,否则staticContext无法被异步组件捕获。需要用到loadable-components提供的loadableReady()和collectChunks()方法:
import React from 'react'; import { renderToString } from 'react-dom/server'; import { StaticRouter } from 'react-router-dom'; import { loadableReady } from '@loadable/component'; import { collectChunks } from '@loadable/server'; import App from './src/App'; app.get('*', async (req, res) => { const staticContext = {}; // 等待所有异步组件加载完成,这一步是关键! await loadableReady(); // 渲染App时传递staticContext const appHtml = renderToString( <StaticRouter location={req.url} context={staticContext}> <App /> </StaticRouter> ); // 收集异步组件的chunk和样式 const { chunks, styles } = collectChunks(appHtml); // 校验staticContext中的状态码,设置响应状态 if (staticContext.statusCode) { res.status(staticContext.statusCode); } // 组装最终的HTML返回 res.send(` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ${styles} </head> <body> <div id="root">${appHtml}</div> ${chunks} </body> </html> `); });
3. 版本兼容性检查
因为你用的是react@16.2.0和react-router@4.2.2,要确保loadable-components的版本和它们兼容。建议安装@loadable/component@5.15.3这个版本(亲测和你的环境兼容),避免用太新的版本(比如v6+可能不支持旧版React):
npm install @loadable/component@5.15.3 @loadable/server@5.15.3
4. 路由配置的注意事项
确保你的路由配置中,异步组件是直接作为component属性的值,而不是嵌套在其他未传递props的组件中:
// 正确的写法 <Route path="/404" component={LoadableNotFound} /> <Route component={LoadableNotFound} /> {/* 匹配所有未定义路由 */} // 错误的写法(会丢失staticContext) <Route path="/404" render={() => <LoadableNotFound />} />
额外排查点
- 检查服务端是否正确引入了
@loadable/server的相关工具,没有的话异步组件的chunk无法被正确收集,也可能间接导致context传递问题。 - 客户端入口也要确保正确初始化loadable-components,比如在
src/index.js中添加:import { loadableReady } from '@loadable/component'; loadableReady(() => { ReactDOM.hydrate(<BrowserRouter><App /></BrowserRouter>, document.getElementById('root')); });
内容的提问来源于stack exchange,提问作者Welly Shen

