如何在 React/redux 中进行服务器端渲染?
Posted
技术标签:
【中文标题】如何在 React/redux 中进行服务器端渲染?【英文标题】:How to do server-side rendering in React/redux? 【发布时间】:2017-08-13 12:26:10 【问题描述】:我是 react/redux 的新手,我对 react/redux 中的服务器端渲染有点困惑, 是的,我在互联网上看到了一些示例,但是当我尝试使用带有外部服务器的模拟 api 时,服务器端渲染不起作用。
cat.js
import React from 'react';
import render from 'react-dom';
import connect from 'react-redux';
import * as mockApi from '../Actions/mockActions';
class Cat extends React.Component
componentWillMount()
this.props.getMockApi();
render()
return(
<div>
Hello Dude
this.props.mock.data.map((data,i) =>
return <li key=i>data.email</li>
)
</div>
)
const mapStateToProps = (state) =>
return
mock:state.mock
;
const mapDispatchToProps = (dispatch) =>
return
getMockApi:() => dispatch(mockApi.getMockData())
;
export default connect(mapStateToProps,mapDispatchToProps)(Cat);
mockActions.js
import axios from 'axios';
import * as types from './actionTypes';
export function getMockData()
return dispatch =>
return axios.get('http://jsonplaceholder.typicode.com/users').then(response =>
dispatch(setThisData(response.data))
)
export function setThisData(data)
return
type:types.MOCK_API,
payload:data
App.js
import React from 'react';
import render from 'react-dom';
import Cat from './components/cat'
import Provider from 'react-redux';
import configureStore from './Store/configureStore';
import createStore ,applyMiddleware,compose from 'redux';
import counterApp from './Reducers'
import thunk from 'redux-thunk';
if(typeof window !== 'undefined')
// Grab the state from a global variable injected into the server-generated html
const preloadedState = window.__PRELOADED_STATE__
// Allow the passed state to be garbage-collected
delete window.__PRELOADED_STATE__
const store = createStore(counterApp, preloadedState, compose(applyMiddleware(thunk)))
render(
<Provider store=store >
<Cat/>
</Provider>
,
document.getElementById('app')
)
devServer.js
import express from 'express';
import path from 'path';
import webpack from 'webpack';
import webpackMiddleware from 'webpack-dev-middleware'
import webpackHotMidleware from 'webpack-hot-middleware';
import bodyParser from 'body-parser';
import React from 'react'
import createStore from 'redux'
import Provider from 'react-redux';
import counterApp from '../../src/client/ReduxServer/Reducers';
import App from '../../src/client/ReduxServer/components/cat';
import renderToString from 'react-dom/server'
import webpackConfig from '../../webpack.config.dev';
let app = express();
app.use(bodyParser.json());
app.use(express.static('public'))
const compiler = webpack(webpackConfig);
app.use(webpackMiddleware(compiler,
hot: true,
publicPath: webpackConfig.output.publicPath,
noInfo: true
));
app.use(webpackHotMidleware(compiler));
// app.get('/*', (req, res) =>
// res.sendFile(path.join(__dirname, '../../index.html'))
// );
//Redux Start
app.use(handleRender);
function handleRender(req,res)
const store = createStore(counterApp);
const html = renderToString(
<Provider store=store >
<App/>
</Provider>
)
const preloadedState = store.getState();
// Send the rendered page back to the client
res.send(renderFullPage(html, preloadedState))
function renderFullPage(html, preloadedState)
console.log(preloadedState)
return `
<!doctype html>
<html>
<head>
<title>Redux Universal Example</title>
</head>
<body>
<div id="app">$html</div>
<script>
window.__PRELOADED_STATE__ = $JSON.stringify(preloadedState).replace(/</g, '\\u003c')
</script>
<script src="bundle.js"></script>
</body>
</html>
`
//Redux Ends
app.listen(3000, () =>
console.log('Listening')
);
现在这只会服务器渲染你好,而不是模拟 Api 调用数据。我知道错过了从服务器端获取数据,但关键是如果我必须渲染两个组件和那个组件,我该怎么办有 5 个 api reuqest,以及如何验证正确的 api 请求
现在我的客户端 Prefecthed 状态将如下所示
window.__PRELOADED_STATE__ = "mock":"data":[]
【问题讨论】:
你为什么不试试mern.io。它具有内置的服务器端渲染。 我想学这些东西 【参考方案1】:好的,为了清楚起见,您已经创建了处理服务器渲染的代码。但是,它没有加载应该获取的数据对吗?
您已经完成了第一步,太棒了!下一步是将实际的动态数据加载到存储中。让我们看看这里的代码
function handleRender(req,res)
const store = createStore(counterApp);
const html = renderToString(
<Provider store=store >
<App/>
</Provider>
)
const preloadedState = store.getState();
// Send the rendered page back to the client
res.send(renderFullPage(html, preloadedState))
发生的事情是您创建了一家商店。该商店用于将 html 呈现为字符串。然后获取存储状态并将其放入 preloadedState。
这是很好的接受 renderToString 将不调用 this.props.getMockApi(); 如你所料。
相反,您必须在调用 renderToString();之前 获取状态;
在这种情况下,您可以执行以下操作。 (请注意,这只是一个示例,您可能希望在生产中使用更通用的东西,特别是如果您使用诸如 react-router 之类的东西。)
import * as mockApi from '../Actions/mockActions';
function handleRender(req, res)
const store = createStore(counterApp);
store.dispatch(mockApi.getMockData())
// And since you used redux-thunk, it should return a promise
.then(() =>
const html = renderToString(
<Provider store=store>
<App/>
</Provider>
)
const preloadedState = store.getState();
// Send the rendered page back to the client
res.send(renderFullPage(html, preloadedState))
);
简单吧? ;D,不,只是在开玩笑。这是反应的一部分,其中还没有真正解决问题的确切方法。
就个人而言,如果我可以选择回到过去,我会告诉自己学习服务器渲染以外的其他东西。我可以使用其他技术,例如代码拆分、延迟加载等。使用服务器渲染,如果 javascript 在用户看到初始页面后很久才到达,他们可能会对其他需要 js 的事情感到沮丧。例如,在我的情况下,一些链接不起作用,一些按钮不起作用,等等。
我并不是说服务器渲染不好。这是一个有趣的技术,只是还有其他更有益于先学习的技术(哦,服务器渲染基本上会锁定你使用 nodejs 作为后端)。祝你好运:)
【讨论】:
很好用,但是如果我使用 react 路由器怎么办? 我要做的基本上是:在每个组件上创建一个静态函数。此函数将保存所有数据调用。在您的 handleRender() 上,使用 react-router 的 match 查找将要渲染的组件列表。之后,调用之前创建的静态函数 嗯,我是初学者,你能在这里回答吗...Question 有很多文章比我能更好地解释这些。例如,https://www.codementor.io/reactjs/tutorial/redux-server-rendering-react-router-universal-web-app 它不起作用,因为action的函数返回的是promise,而不是dispatch!以上是关于如何在 React/redux 中进行服务器端渲染?的主要内容,如果未能解决你的问题,请参考以下文章
react + redux + saga +服务器端渲染+如何停止服务器端渲染页面的额外ajax调用?
如何从服务器端 express/mongo/mongoose 中的 URL,客户端的 axios/react/redux 中获取参数 ID