【问题标题】:Server side rendering with redux使用 redux 进行服务器端渲染
【发布时间】:2017-04-04 02:24:54
【问题描述】:

我有一个用 reactjs 和 redux 构建的简单应用程序。由于我刚开始学习 redux,我在进行服务器端渲染时遇到了一些问题。

到目前为止,我所拥有的是... 客户端 App.js 渲染 App 组件。

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.body
);

一个应用组件:

const App = () => (
  <div>
    <SearchContainer />
    <TodoContainer />
  </div>
)

其中两个项目都是容器 SearchContainer 正在从输入框获取输入,触发一个动作,该动作正在调度带有已加载待办事项的消息(来自远程休息服务)。

现在我喜欢在服务器端的页面加载时加载那些待办事项,并在页面显示后显示它们。我该怎么做?有没有办法在页面实际显示之前不使用服务器端渲染和加载项目?

到目前为止,我得到的是 routes.js 文件:

  const middleware = [ thunk ]
  if (process.env.NODE_ENV !== 'production') {
    middleware.push(createLogger())
  }

  const store = createStore(reducer,applyMiddleware(...middleware))

  app.get('/', function(req, res) {
    res.render('home',{
      markup: ReactDOMServer.renderToString(
        <Provider store={store}>
          <App />
        </Provider>
      )
    });
//Some code calling remote service goes here

这通常是加载和应用程序,但是我如何将加载的数据推送到 TodoContainer 中?它应该是某种发送消息的方式,还是应该使用 todos 数组作为属性来初始化 App 组件?

【问题讨论】:

  • 这个问题的答案对于 * 来说太大/太长了。网上有很多资源,包括官方文档。 redux.js.org/docs/recipes/ServerRendering.html。如果您开始实施解决方案并询问有关细节的问题,会更容易提供帮助。

标签: javascript node.js reactjs redux


【解决方案1】:

您不需要服务器端渲染。

解决您面临的问题的最常见方法是从componentDidMount 加载数据。在返回此数据之前,您需要显示一些内容,例如微调器或颤动器或消息或空白页。就是那样子。您的应用不会是唯一带有加载指示器的应用...

但是,您还可以在服务器端获取数据,然后再发送页面,然后将其发送到 页面中的 &lt;script&gt; 标记和帮助JSON.stringify.

<script>
  window.initialData = {
    "myData": [ 1, 2, 3 ]
  };
</script>

然后您可以使用它来初始化商店。这使数据立即可用,并且不再需要 API 端点。

const store = createStore(reducer, window.initialData, applyMiddleware(...middleware));

您也可以使用这些相同的数据在服务器上进行渲染,但无论如何都不是必需的。

最后我想指出的是,您正在渲染到document.body,这是一个坏主意,因为许多浏览器扩展和插件会根据自己的需要将 div 或其他内容添加到 DOM 中。 React 不能很好地处理这些。它期望控制它传递的元素。最好只创建一个div 并将其渲染到其中。

【讨论】:

    【解决方案2】:

    考虑一下你是否真的需要在 todo 应用中使用 SSR。主要优点是改进了 SEO,并且您的应用程序准备就绪更快(它已经呈现)。如果它不会很重,你可能想跳过它。

    有没有办法在页面实际显示之前不使用服务器端渲染和加载项目?

    是的,例如,您可以在 componentWillMount 中调度获取数据并将其推送到 Redux 状态的操作。在没有数据之前,您可以渲染一些加载指示器的null

    【讨论】:

    • 嗯,这是个问题。第一个屏幕似乎必须是 ssr 页面。否则用户将看到一个空白页面。
    • 是的,但仅限于获取数据的时间(不应超过一秒)。无论如何 - 如果您想在服务器上获取数据,唯一要做的额外事情是在发送渲染标记之前等待异步操作完成。看看这个:github.com/reactjs/redux/issues/99.
    【解决方案3】:

    嗯,我在 react、redux、nodejs 和其他一些客户端和服务器端技术方面获得了一些经验,这就是我发现的以及它对我的工作原理。

    react/redux 应用中的服务器端渲染包括几个步骤: 1.在服务器上加载数据 2. 从服务器推送数据到客户端 3.将数据加载到redux存储中

    这是一个上传“redux-polyglot”组件翻译的小例子。

    routes.js [处理快速服务器请求]:

    //...all of your imports goes here
    import en_phrases from './resources/phrases/en'
    
    app.get('/', async function(req, res) {
    
        const store = createStore(reducers, applyMiddleware(...whatevermiddlewareyouneed))
        return res.render('home', {
                markup:  ReactDOMServer.renderToString(
                  <Provider store={store}>
                    <App/>
                  </Provider>
                ),
                otherPreloadedStateThanPhrases: JSON.stringify(...anythingelse)
                phrases: JSON.stringify(en_phrases)
          });
    }
    

    这里的“home”值是车把模板名称,它具有:

    <script type="application/json" id="phrases">
      {{{ phrases }}}
    </script>
    

    应用程序的职责是从“短语”脚本标签加载数据并将其设置为具有多语言功能的默认语言。

    app.js

    const phrases = JSON.parse(document.getElementById('phrases').innerHTML);
    
    const store = createStore(reducers, otherPreloadedStateThanPhrases, applyMiddleware(...middleware));
    store.dispatch(setLanguage('en', phrases));
    

    LoginContainer.js [应用中使用的示例容器]

    let LoginContainer = ({p}) => {
         <span>{p.tc('login')}</span>
    }
    exports.LoginContainer = connect((state) => ()(translate(LoginContainer))
    

    其中“登录”是从服务器上的短语 json 加载的值: zh.js

    export default {
      login: "Login"
    }
    

    【讨论】: