ssr

什么是ssr:server side render,服务端渲染

不同于jsp,php等传统服务端渲染(这种情况下,前后端代码是分开的,写了两份)

目前的ssr是基于react vue等前端框架的同构渲染(即一份代码,同时运行在server和client端),开发人员只关注业务实现即可

ssr的优缺点就不细说了,无非seo,首屏更快,学习成本变高,加大服务端资源开销等

目前我们项目都逐步采用ssr方案,基于nextjs重构,作为开发人员,对原理比较好奇,研究一下

基本流程

大致的流程如下图

react系统docker部署 react-window_ssr

打包阶段会将业务代码打包两次,一份部署在服务端,一份用于客户端(可传到cdn)

然后启动服务,基于用户请求的路由决定render哪个页面,主要用到renderToStringapi将page组件转化为html标记

最简单的情况,将html标记直接返回客户端,渲染一个静态页面

但实际业务中,一般在服务端需要获取数据,根据数据来生成html,这种情况下,当在客户端重新render时,如何保证数据一致呢,解决办法是将服务端获取到的数据以字符串的形式返回给客户端,客户端渲染的时候直接以该数据进行渲染,保证数据的一致性,进而保证了ui的一致性

当在客户端运行时,主要用hydrateapi将html标记与js代码重新结合,之后就与服务端完全没关系了,可以当spa的情况处理

demo

基于这个流程,简单实现一个demo

准备

服务端

  • 首先需要node服务,该demo采用koa
  • node端实现render逻辑,引入组件,获取数据,将数据传入page组件,并利用renderToString生成标记
  • 返回标记与数据给客户端

客户端

  • 获取服务端数据
  • 将数据传入page组件并利用hydrate render页面

主要代码实现:

server端

// render页面
async function render(Element) {
  // 获取组件初始props,可以在该方法内获取数据
  const props = await Element.getInitProps()

  const html = renderToString(<Element {...props} />)
  const __SSR_DATA__ = {
    props
  }

  return {
    html,
    __SSR_DATA__
  }
}

router.get('/demo', async ctx => {
  // 将生成的标记和属性都转为文本传给客户端
  const { html, __SSR_DATA__ } = await render(Demo)
  ctx.body = `<!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>ssr</title>
  </head>
  <body>
    <script>
      window.__SSR_DATA__ = ${JSON.stringify(__SSR_DATA__)}
    </script>
    <div id="root">${html}</div>
    <script src="http://localhost:9090/index.js"></script>
  </body>
  </html>`
})

客户端

import { hydrate } from 'react-dom';
import Demo from './demo';

// 服务端将props数据挂载到window上,客户端重新渲染时直接传入该属性
const { props } = window.__SSR_DATA__

// hydrate会根据html中已有的标记进行对比,决定是否要重新渲染dom
hydrate(
  <Demo {...props} />,
  document.getElementById('root'),
);

仓库地址

ssr

nextjs

目前react ssr主要是用nextjs

nextjs实现原理与上面一致,增加了路由,缓存,打包,错误处理,页面加载等一系列功能

生产中,除非很简单,或者只为了首屏,不考虑同构,可以自己实现,正常情况,直接使用nextjs即可

后续会记录一下next源码分析

待续…