这一次,让我们来以React为例,把服务端渲染(Server Side Render,简称“SSR”)学个明明白白。

part1:实现一个基础的React组件SSR

这一部分来简要实现一个React组件的SSR。

一. SSR vs CSR

什么是服务端渲染?

废话不多说,直接起一个express服务器。

var express = require('express')
var app = express()

app.get('/', (req, res) => {
 res.send(
 `
   <html>
     <head>
       <title>hello</title>
     </head>
     <body>
       <h1>hello</h1>
       <p>world</p>
     </body>
   </html>
 `
 )
})

app.listen(3001, () => {
 console.log('listen:3001')
})

启动之后打开localhost:3001可以看到页面显示了hello world。而且打开网页源代码如下:

也能够完成显示。
这就是服务端渲染。其实非常好理解,就是服务器返回一堆html字符串,然后让浏览器显示。
与服务端渲染相对的是客户端渲染(Client Side Render)。那什么是客户端渲染?
现在创建一个新的React项目,用脚手架生成项目,然后run起来。
这里你可以看到React脚手架自动生成的首页。


然而打开网页源代码,我们会发现网页并没有任何DOM结构

body中除了兼容处理的noscript标签之外,只有一个id为root的标签。那首页的内容是从哪来的呢?很明显,是下面的script中拉取的JS代码控制的。
因此,CSR和SSR最大的区别在于前者的页面渲染是JS负责进行的,而后者是服务器端直接返回HTML让浏览器直接渲染。
为什么要使用服务端渲染呢?

传统CSR的弊端:

  1. 由于页面显示过程要进行JS文件拉取和React代码执行,首屏加载时间会比较慢。
  2. 对于SEO(Search Engine Optimazition,即搜索引擎优化),完全无能为力,因为搜索引擎爬虫只认识html结构的内容,而不能识别JS代码内容。

SSR的出现,就是为了解决这些传统CSR的弊端。

二、实现React组件的服务端渲染

刚刚起的express服务返回的只是一个普通的html字符串,但我们讨论的是如何进行React的服务端渲染,那么怎么做呢? 首先创建containers文件夹,新建一个Home.js的文件,写一个简单的React组件:

// containers/Home.js
import React from 'react';
const Home = () => {
  return (
    <div>
      <div>This is zu3zz</div>
    </div>
  )
}
export default Home

现在的任务就是将它转换为html代码返回给浏览器。
总所周知,JSX中的标签其实是基于虚拟DOM的,最终要通过一定的方法将其转换为真实DOM。虚拟DOM也就是JS对象,可以看出整个服务端的渲染流程就是通过虚拟DOM的编译来完成的,因此虚拟DOM巨大的表达力也可见一斑了。
而react-dom这个库中刚好实现了编译虚拟DOM的方法。做法如下:

// server/index.js
import express from 'express';
import { renderToString } from 'react-dom/server';
import Home from './containers/Home';

const app = express();
const content = renderToString(<Home />);
app.get('/', function (req, res) {
   res.send(
   `
    <html>
      <head>
        <title>ssr</title>
      </head>
      <body>
        <div id="root">${content}</div>
      </body>
    </html>
   `
   );
})
app.listen(3001, () => {
  console.log('listen:3001')
})

启动express服务,再浏览器上打开对应端口,页面显示出"this is sanyuan"。
到此,就初步实现了一个React组件是服务端渲染。
当然,这只是一个非常简陋的SSR,事实上对于复杂的项目而言是无能为力的,在之后会一步步完善,打造出一个功能完整的React的SSR框架。