Drcus | 王亚振

Drcus | 王亚振

随便写,记录点东西

React 服务端渲染

发布于:  

server

服务端渲染主要为客户端提供一个初始state 通过import ReactDOM from 'react-dom/server';获取ReactDOM对象 用来将组件渲染为HTML字符串

一下是一个用Express搭建的服务端

import 'babel-core/polyfill';
import path from 'path';
import express from 'express';
import React from 'react';
import ReactDOM from 'react-dom/server';
import Router from './routes';
import Html from './components/Html';
import assets from './assets.json';
import createStore from './stores/create';
import {Provider} from 'react-redux';
const debug = require('debug')('hedge:server');

const DevTools = require('./components/DevTools/DevTools');

const server = global.server = express();
const port = process.env.PORT || 5000;
server.set('port', port);

//
// Register Node.js middleware
// -----------------------------------------------------------------------------
server.use(express.static(path.join(__dirname, 'public')));

server.get('*', async (req, res, next) => {
  try {
    let statusCode = 200;
    const data = { title: '', description: '', css: '', body: '', entry: assets.app.js, bodyClassName: '' };
    const css = [];
    const context = {
      insertCss: styles => css.push(styles._getCss()),
      onSetTitle: value => data.title = value,
      onSetMeta: (key, value) => data[key] = value,
      onPageNotFound: () => statusCode = 404,
      onSetBodyClassName: className => data.bodyClassName = className
    };

    let currentMenu = { currentMenu: { currentMenu: req.path.substr(1) } };

    const store = createStore(currentMenu);
    await Router.dispatch({ path: req.path, context }, (state, component) => {
      data.body = ReactDOM.renderToString(
        <Provider store={store} key="provider">
          <div>
            {component}
            <DevTools />
          </div>
        </Provider>
      );
      data.css = css.join('');
    });

    debug('do server side render [%s]', req.path);
    const html = ReactDOM.renderToStaticMarkup(<Html {...data} store={store}/>);
    res.status(statusCode).send('<!doctype html>\n' + html);
  } catch (err) {
    next(err);
  }
});

// 运行
server.listen(port, () => {  
  console.log(`The server is running at http://localhost:${port}/`);
});

通过以上代码可以看到通过ReactDOM.renderToStaticMarkup最终返回的字符串. 还有一点服务器的state要保存到window.__INITIAL_STATE__ 如:

<body className={this.props.bodyClassName}>
    <div id="app" dangerouslySetInnerHTML={{ __html: this.props.body }} />    
    <script dangerouslySetInnerHTML={{ __html: `window.__INITIAL_STATE__=${serialize(this.props.store.getState())};` }} charSet="UTF-8"/>
    <script src={this.props.entry}></script>
    <script dangerouslySetInnerHTML={this.trackingCode()} />
</body>

客户端

客户端通过window.__INITIAL_STATE__来装载服务端返回的初始state 示意代码

const store = createStore(window.__INITIAL_STATE__);
function render(state) {
 
  Router.dispatch(state, (newState, component) => {
    ReactDOM.render(
      <Provider store={store} key="provider">
        <div>
          {component}
          <DevTools />
        </div>
      </Provider>,
      appContainer
    );
  });
}

厚颜一下 ~^_^~

赏赐