详解React服务端渲染从入门到精通

这篇文章是我自己在搭建个人网站的过程中,用到了服务端渲染,看了一些教程,踩了一些坑。想把这个过程分享出来。
我会尽力把每个步骤讲明白,将我理解的全部讲出来。

文中的示例代码来自于这个仓库,也是我正在搭建的个人网站,大家可以一起交流一下。示例代码因为简化,与仓库代码有些许出入

本文中用到的技术
React V16 | React-Router v4 | Redux | Redux-thunk | express

React 服务端渲染

服务端渲染的基本套路就是用户请求过来的时候,在服务端生成一个我们希望看到的网页内容的HTML字符串,返回给浏览器去展示。

浏览器拿到了这个HTML之后,渲染出页面,但是并没有事件交互,这时候浏览器发现HTML中加载了一些js文件(也就是浏览器端渲染的js),就直接去加载。

加载好并执行完以后,事件就会被绑定上了。这时候页面被浏览器端接管了。也就是到了我们熟悉的js渲染页面的过程。

需要实现的目标:

React组件服务端渲染

路由的服务端渲染

保证服务端和浏览器的数据唯一

css的服务端渲染(样式直出)

一般的渲染方式

服务端渲染:服务端生成html字符串,发送给浏览器进行渲染。

浏览器端渲染:服务端返回空的html文件,内部加载js完全由js与css,由js完成页面的渲染

优点与缺点

服务端渲染解决了首屏加载速度慢以及seo不友好的缺点(Google已经可以检索到浏览器渲染的网页,但不是所有搜索引擎都可以)

但增加了项目的复杂程度,提高维护成本。

如果非必须,尽量不要用服务端渲染

整体思路

需要两个端:服务端、浏览器端(浏览器渲染的部分)

第一: 打包浏览器端代码

第二: 打包服务端代码并启动服务

第三: 用户访问,服务端读取浏览器端打包好的index.html文件为字符串,将渲染好的组件、样式、数据塞入html字符串,返回给浏览器

第四: 浏览器直接渲染接收到的html内容,并且加载打包好的浏览器端js文件,进行事件绑定,初始化状态数据,完成同构

React组件的服务端渲染

让我们来看一个最简单的React服务端渲染的过程。

要进行服务端渲染的话那必然得需要一个根组件,来负责生成HTML结构

import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.hydrate(<Container />, document.getElementById('root'));

当然这里用ReactDOM.render也是可以的,只不过hydrate会尽量复用接收到的服务端返回的内容,

来补充事件绑定和浏览器端其他特有的过程

引入浏览器端需要渲染的根组件,利用react的 renderToString API进行渲染

import { renderToString } from 'react-dom/server' import Container from '../containers' // 产生html const content = renderToString(<Container/>) const html = ` <html> <body>${content}</body> </html> ` res.send(html)

在这里,renderToString也可以替换成renderToNodeStream,区别在于前者是同步地产生HTML,也就是如果生成HTML用了1000毫秒,

那么就会在1000毫秒之后才将内容返回给浏览器,显然耗时过长。而后者则是以流的形式,将渲染结果塞给response对象,就是出来多少就

返回给浏览器多少,可以相对减少耗时

路由的服务端渲染

一般场景下,我们的应用不可能只有一个页面,肯定会有路由跳转。我们一般这么用:

import { BrowserRouter, Route } from 'react-router-dom' const App = () => ( <BrowserRouter> {/*...Routes*/} <BrowserRouter/> )

但这是浏览器端渲染时候的用法。在做服务端渲染时,需要使用将BrowserRouter 替换为 StaticRouter
区别在于,BrowserRouter 会通过HTML5 提供的 history API来保持页面与URL的同步,而StaticRouter
则不会改变URL

import { createServer } from 'http' import { StaticRouter } from 'react-router-dom' createServer((req, res) => { const html = renderToString( <StaticRouter location={req.url} context={{}} > <Container /> <StaticRouter/>) })

这里,StaticRouter要接收两个属性:

location: StaticRouter 会根据这个属性,自动匹配对应的React组件,所以才会实现刷新页面,服务端返回的对应路由的组与浏览器端保持一致

context: 一般用来传递一些数据,相当于一个载体,之后讲到样式的服务端渲染的时候会用到

Redux同构

数据的预获取以及脱水与注水我认为是服务端渲染的难点。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/f3e6aa452a84db9f182252021d16a6e8.html