Skip to content

react服务器渲染入门 #9

@iscarecrow

Description

@iscarecrow

面临的问题及解决方案

服务器渲染遇到了以下几个问题。

  • 判断运行环境,差异化加载资源
  • 解决路由共享问题
  • 解决initial data 问题
  • 服务器 render方法
  • 服务器运行es6

解决方案

1.判断运行环境

因为部分依赖的js不是渲染用,可是其他逻辑。比如以下jssdk。是对接客户端app端方法。在服务器渲染时,不需要依赖加载。所以判断环境是否是window

// 浏览器加载
if ( 'undefined' !== typeof window ) {
  require('../utils/dtSdk');
}
2.共享路由

routes.js

import { Route } from "react-router";
import React from "react";

export default (
  <Route name="app" path="/" component={App}>
      <Route path="a" component={a} />
      <Route path="*" component={error404}/>
  </Route>
);

server.js 见 代码一

获取请求的url,react-router提供的match方法可以捕获此url。进入router。之后进入请求逻辑,获取数据。

app.get('/*', function (req, res) {
  //请求
  const location = createLocation(req.url);
  //匹配路由
  match({ routes, location }, (err, redirectLocation, renderProps) => {
  //获取数据
  // 数据填充store
  // render
   })
});

client.js


React.render(
  <Provider store={store}>
    {() =>
        <ReduxRouter>
          <Router children={routes} history={history} />
        </ReduxRouter>
    }
  </Provider>,
  document.getElementById('root')
);
3.初始化数据

html是react+data的dom,initialState是在页面的store

const renderFullPage = (html, initialState) => {
  return `
    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8"/>
        <link rel="stylesheet" type="text/css" href="/static/app.css">
      </head>
      <body>
        <div id="root">${html}</div>
        <script>
          window.__INITIAL_STATE__ = ${JSON.stringify(initialState)}; 
        </script>
        <script src="/static/bundle.js"></script>
      </body>
    </html>
  `;
}
4.服务器 render方法, 见代码一

react 有renderToString的api

const InitialView = (
    <Provider store={store}>{() =>
	  <RoutingContext {...renderProps}/>
            }
   </Provider>
);

const componentHTML = React.renderToString(InitialView);
5.服务器运行es6

使用babel-core/register

require('babel-core/register');
require('./server');

服务器代码示例,代码一

app.get('/*', function (req, res) {

  const location = createLocation(req.url);

  fetchInventoryByIds(user => {

      if(!user) {
        return res.status(401).end('Not Authorised');
      }

      match({ routes, location }, (err, redirectLocation, renderProps) => {

        if(err) {
          console.error(err);
          return res.status(500).end('Internal server error');
        }

        if(!renderProps)
          return res.status(404).end('Not found');

        const store = configureStore({
          user : user, 
          version : packagejson.version,
          NewSingleGoodsData: user.NewSingleGoodsData
        });

        const InitialView = (
          <Provider store={store}>
            {() =>
              <RoutingContext {...renderProps} />
            }
          </Provider>
        );
  fetchComponentDataBeforeRender(store.dispatch, renderProps.components, renderProps.params)
          .then(html => {
            const componentHTML = React.renderToString(InitialView);
            const initialState = store.getState();
            res.status(200).end(renderFullPage(componentHTML,initialState))
          })
          .catch(err => {
            console.log(err)
            res.end(renderFullPage("",{}))
          });
      });

    }
  )

});

项目结构

保证主业务逻辑服务器端和客户端皆可用

+-- client
|   +-- index.js(客户端的app.js入口)
+-- common
|   +-- containers
|   +-- compenents
|   +-- actions
|   +-- reducers
|   +-- router
|   +--(react+redux+react-router业务逻辑)
+-- server
|   +-- server

结果

渲染前后的差别的差别。不做服务器渲染,dom内容是通过客户端js渲染完成。传输的html只是一个空模版。服务器渲染后会把内容填满后提供完整的html

渲染前的html
<!doctype html>
    <html>
      <head>
        <link rel="stylesheet" type="text/css" href="/static/app.css">
      </head>
      <div id="root"></div>
      <body>
<script src="/static/bundle.js"></script>
  </body>
</html>
服务器渲染后的html
 <!doctype html>
    <html>
      <head>
        <link rel="stylesheet" type="text/css" href="/static/app.css">
      </head>
      <body>
        <div id="root">
        <section data-reactid=".29kxietvhmo" data-react-checksum="861612904"><section data-reactid=".29kxietvhmo.0"><ul data-reactid=".29kxietvhmo.0.0"><li class="cp-nsgoods-item" data-reactid=".29kxietvhmo.0.0.$0"><div class="pg-new-single-goods" data-reactid=".29kxietvhmo.0.0.$0.0"><div class="pg-new-single-goods-time" data-reactid=".29kxietvhmo.0.0.$0.0.0"><span data-reactid=".29kxietvhmo.0.0.$0.0.0.0">2016</span><span data-reactid=".29kxietvhmo.0.0.$0.0.0.1">年</span><span data-reactid=".29kxietvhmo.0.0.$0.0.0.2">4</span><span data-reactid=".29kxietvhmo.0.0.$0.0.0.3">月</span><span data-reactid=".29kxietvhmo.0.0.$0.0.0.4">26</span><span data-reactid=".29kxietvhmo.0.0.$0.0.0.5">日 </span><span data-reactid=".29kxietvhmo.0.0.$0.0.0.6">星期二</span></div><section class="swiper-banners" data-reactid=".29kxietvhmo.0.0.$0.0.1"><div class="swiper-container0 swiper-container" data-reactid=".29kxietvhmo.0.0.$0.0.1.0"><div class="swiper-wrapper" data-reactid=".29kxietvhmo.0.0.$0.0.1.0.0"><a href="http://www.duitang.com/guide2/buy_group_purchase/2016042601/?__urlopentype=pageweb" data-background="http://img4q.duitang.com/uploads/people/201604/25/20160425142354_tFnhU.thumb.800_0.jpeg" class="swiper-lazy swiper-slide" data-reactid=".29kxietvhmo.0.0.$0.0.1.0.0.$0">
        </div>
   <script src="/static/bundle.js"></script>
  </body>
</html>

待解决的点??

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions