React如何在Server Side Rendering中预取数据(二)

前一篇文章讲到了为了预取数据,各个组件的写法。这里从整体上讲一个client和server分别应该怎么做。
Server Side Rendering的一个明确目标其实就是等“异步”操作都结束了,再renderToString然后返回给客户端。这样,客户端没有javascript的情况下,依然可以看到数据(所以对爬虫是友好的)。
我用到的库是 react-redux, react-router, redux-saga,所以是要让redux-saga能够处理完必要的请求之后,进行第二次渲染,然后返回给客户端。(用redux-thunk是一样的道理, 需要等promise结束之后,再调用renderToString,然后返回给客户端)

废话不多说,下面是样例代码:

// express 处理请求入口
app.get('*', (req, res) => {
  handleRender(req, res)
})

// 根据你自己的需求创建store, 主要参考redux就行了
function  createStoreForServer() {
  const sagaMiddleware = createSagaMiddleware()
  middlewares = [sagaMiddleware] // 根据需求自己可以加入其它中间件
  let preloadedState = {} // 客户端需要在这个地方加载服务器端传过来的初始状态, 详见redux文档(http://redux.js.org/docs/recipes/ServerRendering.html)

  let store = createStore(rootReducer, {}, applyMiddleware(...middlewares))
  
  // 下面是关键点, 这些方法是server端需要用到的
  store.runSaga = () => sagaMiddleware.run(rootSaga)
  store.close = () => store.dispatch(END)

  return store
}
 
function handleRender(req, res) {
  let store = createStoreForServer()

  // 判断saga的调用都结束了, 然后开始第二次渲染
  store.runSaga().done.then(() => {
    const html = renderToString()
    res.send(renderFullPage(html, store.getState())) // renderFullPage 参见redux文件就行了
  }

  // 触发第一次渲染, 可是返回值我们并不关心, 只要改变store即可
  renderToString()
 
  // 关停saga, 第二次渲染的时候,忽略各种请求就好啦
  store.close()
}

上面这些一做,基本上就搞定啦。服务器端渲染,只需引入了这么一小段代码,就可以解决核心问题了。

我这里没有提到的问题还有(每个小点感觉都可以专门写一篇博客了):

  1. 取用户私有数据怎么办? 靠cookie。如何做呢?我自己的实现并不完美,是把一些信息暂时放在store中了,但是react-cookie可能有更好的解决办法(我一时半会没搞清楚怎么跟redux结合,就没启用)。
  2. 官方文档中,renderFullPage中需要把html的结构直接写在函数里面,可是html的内容可能是动态生成的,怎么办?首先,为啥会动态生成呢, 因为生产环境打包的时候,js, css是需要带hash的,因此html引用的js, css的名字会变化。动态生成我用了HtmlWebpackPlugin。其次, 有个html文件作为模板了,怎么用renderFullPage? 我用的是个超简单又笨的方法:读取html文件,然后字符串替换。
  3. react-router在server,client需要用到不用的类型的history,怎么处理?这个在react-router的github上搜一搜就解决方案。 我用的是BrowserRouterStaticRouter,注意StaticRouter可能有重定向信息就是了。

最后的最后, 有人给我推荐过next.js,据说可以方便解决SSR问题,我大概看了一下, 跟原生的写法还是有一些不同的,如果新项目,可以考虑在开始的时候就启用。这次我踩的坑已经够多,就没有去用next.js了(也是有点小担心react 16.0可能跟next.js会不兼容,导致我到时候不能顺畅升级)。

你可能感兴趣的:(React如何在Server Side Rendering中预取数据(二))