ssr介绍
ssr 是什么
服务器端渲染(ssr):用户请求服务器,服务器上直接生成 HTML 内容并返回给浏览器。
ssr实现原理 - virtual Dom 的存在
在服务器端,判断是服务器环境,通过操作js对象,把virtual Dom通过renderToString 或 renderToStaticMarkup映射成字符串输出。通过url请求,在真实浏览器环境把virtual Dom映射成真实Dom,完成页面挂载。
react 通过renderToString 或 renderToStaticMarkup 都可以实现服务端渲染
- renderToString:将 React Component 转化为 HTML 字符串,生成的 HTML 的 DOM 会带有额外属性:各个 DOM 会有data-react-id属性,第一个 DOM 会有data-checksum属性。
- renderToStaticMarkup:同样是将 React Component 转化为 HTML 字符串,但是生成 HTML 的 DOM 不会有额外属性,从而节省 HTML 字符串的大小。
ssr 优点
- ssr 首屏加载更快,不会像客户端渲染一样,在下载main.js的时候出现白屏,有更好的用户体验
- seo: 传统的搜索引擎爬虫因为不能抓取JS生成后的内容,遇到单页web项目,抓取到的内容啥也没有。在SEO上会吃很多亏,很难排搜索引擎到前面去
ssr 缺点
-
理论上,SSR(包括传统的服务端渲染)最大的瓶颈就是服务端的性能
如果用户规模大,SPA本身就是一个大型分布式系统,充分利用用户的设备去运行JS的运算,SSR则是把这些工作包揽到自己的服务器上。所以对于需要大量计算(图表特别多)而且用户量巨大的页面,并不太适合,但SSR非常适合于大部分的内容展示页面 -
项目复杂度增加,需要前端团队有较高的技术素养
为了同构要处处兼容 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。