React SSR + express 构建简单服务端渲染Demo

5,374 阅读3分钟

ssr介绍

ssr 是什么

服务器端渲染(ssr):用户请求服务器,服务器上直接生成 HTML 内容并返回给浏览器。

ssr实现原理 - virtual Dom 的存在

在服务器端,判断是服务器环境,通过操作js对象,把virtual Dom通过renderToString 或 renderToStaticMarkup映射成字符串输出。通过url请求,在真实浏览器环境把virtual Dom映射成真实Dom,完成页面挂载。

react 通过renderToString 或 renderToStaticMarkup 都可以实现服务端渲染

  1. renderToString:将 React Component 转化为 HTML 字符串,生成的 HTML 的 DOM 会带有额外属性:各个 DOM 会有data-react-id属性,第一个 DOM 会有data-checksum属性。
  2. renderToStaticMarkup:同样是将 React Component 转化为 HTML 字符串,但是生成 HTML 的 DOM 不会有额外属性,从而节省 HTML 字符串的大小。

ssr 优点

  1. ssr 首屏加载更快,不会像客户端渲染一样,在下载main.js的时候出现白屏,有更好的用户体验
  2. seo: 传统的搜索引擎爬虫因为不能抓取JS生成后的内容,遇到单页web项目,抓取到的内容啥也没有。在SEO上会吃很多亏,很难排搜索引擎到前面去

ssr 缺点

  1. 理论上,SSR(包括传统的服务端渲染)最大的瓶颈就是服务端的性能
    如果用户规模大,SPA本身就是一个大型分布式系统,充分利用用户的设备去运行JS的运算,SSR则是把这些工作包揽到自己的服务器上。所以对于需要大量计算(图表特别多)而且用户量巨大的页面,并不太适合,但SSR非常适合于大部分的内容展示页面

  2. 项目复杂度增加,需要前端团队有较高的技术素养
    为了同构要处处兼容 Node.js 不同的执行环境,不能有浏览器相关的原生代码在服务端执行,前端代码使用的 window 在 node 环境是不存在的,所以要 mock window,其中最重要的是 cookie,userAgent,location

ssr搭建

create-react-app + express

    npx create-react-app ssr
    cd ssr
    npm install express
    mkdir server 
    cd server
    touch server.js
    touch index.js

server.js 中 写入下面的代码

import path from 'path'
import fs from 'fs'

import express from 'express'
import React from 'react'
import ReactDOMServer from 'react-dom/server'

import App from '../src/App'

const PORT = 8080
const app = express()

const router = express.Router()

const serverRenderer = (req, res, next) => {
  fs.readFile(path.resolve('./build/index.html'), 'utf8', (err, data) => {
    if (err) {
      console.error(err)
      return res.status(500).send('An error occurred')
    }
    return res.send(
      data.replace(
        '<div id="root"></div>',
        `<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`
      )
    )
  })
}
router.use('^/$', serverRenderer)

router.use(
  express.static(path.resolve(__dirname, '..', 'build'), { maxAge: '30d' })
)

// tell the app to use the above rules
app.use(router)

// app.use(express.static('./build'))
app.listen(PORT, () => {
  console.log(`SSR running on port ${PORT}`)
})

./public/index.html<div id="root"></div> 替换成
<div id="root">\${ReactDOMServer.renderToString(<App />)}</div>

src/index.js 替换 ReactDOM.render(<App />, document.getElementById('root'))ReactDOM.hydrate(<App />, document.getElementById('root'))

在node 环境下 所有的js 代码都要转化成jsx代码 才能运行raect环境 ,所以需要 babel转义 npm install @babel/register @babel/preset-env @babel/preset-react ignore-styles

server/index.js 中写入

require('ignore-styles')

require('@babel/register')({
  ignore: [/(node_modules)/],
  presets: ['@babel/preset-env', '@babel/preset-react']
})

require('./server')

先打包 npm run build
运行 node server/index.js

效果

ssr的script 脚本 和 html 和csr的都是在node 服务端生成的。所以 较csr没有了 html的备注,csr里的script都是打包形成的bundle。

参考链接

  1. Server Side Rendering with React
  2. An Introduction to React Server-Side Rendering
  3. 后端渲染、客户端渲染(CSR)、同构应用(SSR)