如何在 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调用?

使用 redux 和 react 进行异步服务器端渲染

React/Redux 服务端渲染初始状态

react+redux教程redux服务端渲染流程

在 react/redux 中的 post 请求后渲染视图

如何从服务器端 express/mongo/mongoose 中的 URL,客户端的 axios/react/redux 中获取参数 ID