ssr
什么是 ssr:server side render,服务端渲染
不同于 jsp,php 等传统服务端渲染(这种情况下,前后端代码是分开的,写了两份)
目前的 ssr 是基于 react vue 等前端框架的同构渲染(即一份代码,同时运行在 server 和 client 端),开发人员只关注业务实现即可
ssr 的优缺点就不细说了,无非 seo,首屏更快,学习成本变高,加大服务端资源开销等
目前我们项目都逐步采用 ssr 方案,基于 nextjs 重构,作为开发人员,对原理比较好奇,研究一下
基本流程
大致的流程如下图
打包阶段会将业务代码打包两次,一份部署在服务端,一份用于客户端(可传到 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 源码分析
待续 …