草庐IT

javascript - 如何在 React/redux 中进行服务器端渲染?

coder 2023-05-30 原文

我是 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":[]}}

最佳答案

好的,为了清楚起见,您已经创建了处理服务器渲染的代码。但是,它没有加载应该获取的数据对吗?

您已经完成了第一步,太棒了!下一步是将实际的动态数据加载到存储中。让我们看看这里的代码

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,不,只是在开玩笑。这是 react 的一部分,其中还没有真正解决问题的确切方法。

就个人而言,如果我可以选择回到过去,我会告诉自己学习服务器渲染以外的其他东西。我可以使用其他技术,例如代码拆分、延迟加载等。使用服务器渲染,如果 javascript 在用户看到初始页面后很久才到达,他们可能会对其他需要 js 的事情感到沮丧。例如,在我的情况下,一些链接不起作用,一些按钮不起作用,等等。

我并不是说服务器渲染不好。这是一个有趣的技术,只是有其他技术更有益于先学习(哦,服务器渲染基本上会锁定你使用 nodejs 作为后端)。祝你好运:)

关于javascript - 如何在 React/redux 中进行服务器端渲染?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42933875/

有关javascript - 如何在 React/redux 中进行服务器端渲染?的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  3. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  4. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  5. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  6. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  7. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  8. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  9. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

    在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

  10. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

随机推荐