javascript - (同构)路由和基于ajax的react组件的后端渲染
问题描述
我正在尝试构建一个同构反应应用程序。我使用 express 作为后端。我有一个带有 App 组件的静态路由器。我捕捉到任何请求并像这样使用 renderToString:
(下面的代码不完整,可能有语法错误 - 没有通过 IDE 运行)
server.jsx:
app.get("/*", (req, res) => {
const itemIDs =
['myFirstItemID', 'mySecondItemID', 'myThirdItemID'] // fetched from DB
const renderedApp =
ReactDOM.renderToString(<StaticRouter> <App itemIDs=itemIDs /> </StaticRouter>);
res.render("index", { renderedData: renderedApp });
res.end();
}
App.jsx - 仅渲染方法
render() {
const renderedRoutes = this.props.itemIDs.map(itemID => (<Route path={itemID} render={() => <Item ItemID={itemID}/>} />)
);
return (
<Switch>
{renderedRoutes}
<Route component={ItemsContainer itemIDs=itemIDs} />
</Switch>
)
}
ItemsContainer.jsx - 仅渲染方法
render() {
const ItemSummaries = this.props.itemIDs.map(itemId => <ItemPreview itemId={itemId}/>)
return (
<div>
{ItemSummaries}
</div>
)
ItemPreview.jsx - 仅渲染方法
render() {
return (
<div>
<Link to={"/" + this.props.itemId}>
<h2>an item</h2>
</Link>
</div>
)
Item.jsx - componendDidMount 和渲染
componentDidMount() {
axios.get("/api/items?itemID=" + this.props.itemID)
.then((data) => this.state.itemData = data);
render() {
return (
<div>
//use itemData here
</Link>
</div>
)
Client.jsx - 提供给客户端(使用相同的 App.jsx 组件)
const MyRoutedApp = () =>
(<BrowserRouter>
<App />
</BrowserRouter>);
ReactDOM.hydrate(<MyRoutedApp/>, document.getElementById('root'));
我觉得我的方法有些不对劲,但我不确定是什么。我遇到的问题是:
- ItemID 仅在服务器内用于生成路由。一旦应用程序在客户端可用,我不再有路由,因此 BrowserRouter 不起作用。我可以使用 ajax 获取路由,但这会导致在我获得所需数据之前进行初始渲染,这导致无论我在哪个 URL 上总是看到几帧的 ItemsContainer(默认路由)。
- 为了 renderToString 一个依赖于 componentDidMount 中的 ajax 来决定其 render() 内容(Item.jsx)的组件,我有哪些选择?
- 最后但并非最不重要的一点 - 这实际上是一个好方法吗?
解决方案
因此,我发现解决第一个问题的一种方法是在初始索引页面响应中(在<script>
标记内)渲染路由,然后让 App 构造函数根据渲染的位置处理路由:
- 如果由服务器渲染 - 获取传递的道具
- 如果在浏览器中呈现 - 获取全局对象。
这是它在代码中的样子:
索引页面中的某处(EJS 模板):
//
<script>const globalRoutes = <%- JSON.stringify(renderedRoutes) %> </script>
//
“/”请求处理程序:
var fetchedRoutes; //your routes
app.get("/*", (req, res) => {
var context = {};
const routedApp = ReactDOMServer.renderToString(
React.createElement(
StaticRouter,
{ location: req.url, context: context },
React.createElement(App, {routesData: fetchedRoutes})
)
);
res.render("index", { renderedData: routedApp, renderedRoutes: fetchedRoutes });
res.end();
});
App如何处理路由:
class App extends React.Component {
constructor(props) {
super(props);
this.state = { routesData: this.isBrowser() ? globalRoutes : this.props.routesData }
}
推荐阅读
- azure - 可以将 Azure DevOps Boards Shared Queries 复制到另一个项目吗?
- amazon-cloudfront - 修复使用 Lambda@Edge 处理漂亮 URL 时的 Cloudfront 403 Access Denied 请求错误
- ios - swift中CACornerMask和UIRectCorner的区别
- node.js - Mongoose:使用“1”作为 Schema.Types.ObjectId 的值创建模型
- javascript - 达到 100% 后无法在 React 中重新加载进度条
- pandas - Pandas:如何对数据框中的一系列列进行排序?
- c# - 可以在覆盖方法中撤销必需属性吗?
- python - Pandas:从 github 读取 csv
- javascript - Javascript:在箭头函数中声明的变量不能在while循环中更新
- python - Django,在上传时重命名图像文件