You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
//初始化客户端数据exportconstgetClientStore=()=>{//客户端数据脱水,获取服务端缓存的数据,然后进行其他处理constpreloadedState=window.PRE_LOADED_STATE//...}//组件中判断是否存在数据,不存在则请求functionHome({
getData, data
}){useEffect(()=>{if(!data){getData()}},[])}
预渲染 / SSG
如果预先知道某些路由所需的路由和数据,我们可以使用与生产环境 SSR 相同的逻辑将这些路由页面预先渲染成静态 的HTML 。这也被视为一种静态站点生成(SSG)的形式。
前言
服务端渲染是什么?我们什么情况下需要使用它?想要了解这些,需要简单聊聊前端渲染的发展史。
早先的服务端渲染
服务端渲染并不是什么新兴的技术,动态网页技术(
PHP
、jsp
等)其实就是服务端渲染,网页都是由后端获取数据并将其放入到网页模板中,然后返回完整的HTML到浏览器渲染。这样做的渲染方式有明显的缺点,每次数据改变都需要重新再去获取数据并组装新的HTML、网页和后端逻辑耦合等。直到AJAX
的出现。客户端渲染爆发
AJAX
的出现,使得前后端得以分离。在该模式下,后端依然会返回一个HTML页面,后续通过AJAX
来动态获取数据,利用DOM操作动态更新网页内容。这意味着可以在不重载整个页面的情况下,对网页的某些部分进行更新,减少带宽,提高性能,也赋予了页面更丰富的展示,越来越复杂的前端工程也催生了MVC
渲染框架(React
、Vue
和AngularJS
),后端可以返回一个空白HTML页面,通过JS脚本进行动态生成内容(单页面应用SPA
),这就是客户端渲染。新的服务端渲染
SPA
看起来已经是最优解,但是它对SEO
不是很友好,且随着应用越来越复杂,JS
代码文件也越来越大,导致首屏渲染的速度明显下降。所以聪明的人们又想到了服务端渲染的方式,那我们直接又使用以前的服务端渲染的方式?显然是不可能,一个是前后端分离解耦是大势不可逆转(不当切图仔!!!),而且我们有了新的技术:Node
和渲染框架(React
、Vue
和AngularJS
),前者让前端开发可以在服务端编写JS
代码,而后者让前端可以一套代码运行在客户端和服务端(同构,后面会细讲),减少了代码量,果然事物的发展是螺旋上升的。现在可以回答上面提出的两个问题了,服务端渲染不是新概念,就是后端组装完整的HTML页面返回到浏览器渲染;之所以需要它是客户端渲染存在缺陷,是否使用该技术需要评估项目的适用性。下面总结一下服务端渲染的优缺点:
SEO
,爬虫更容易爬取举一个简单的
SSR
例子:React服务端渲染
实现React服务端渲染
通过前言,我们了解到服务端渲染其实就是将动态JS生成页面转化为静态HTML输出到浏览器,也就是说我们需要将React组件转为HTML,且需处理绑定在JSX代码上的事件等交互,因为我们的应用还是
SPA
模式,所以还需要考虑兼容客户端渲染的情况。将React组件转换为HTML字符串
首先是转化成HTML,使用的API为:
ReactDOMServer.renderToString(element)
,使用该方法可以将React元素转化为HTML字符串,这样我们就可以在服务端组装成HTML。同构
同构,简单点说就是一份代码,双端运行,即我们的前端代码,既支持在服务端中组装HTML的,也支持在客户端中动态渲染HTML。
如上文通过
renderToString
方法完成了服务端渲染的第一步,但是该方法不会处理事件点击等交互行为,这时候就需要通过客户端来完成了。同构就了解决这一问题,比如上面的App
组件,我们还需要引入客户端处理的JS代码:ReactDOM.hydrate
也被叫做“注水”,意思就是在服务端渲染后,React 将保留页面渲染的内容,只对事件绑定等客户端内容进行特殊处理。除了渲染交互同构,我们还要实现双端的路由同构,即页面可以通过点击页面按钮跳转,也支持输入链接进行跳转:
BrowserRouter
StaticRouter
如上面同构的
App
例子,可以这样修改:小结一下:新的服务端渲染,采用同构的的方式,由服务端完成页面的 HTML 结构拼接,发送到浏览器,然后再进行一次客户端的处理,为其绑定状态与事件,成为完全可交互页面。
创建React SSR项目
接下来,就开始创建我们的SSR项目了。
按照上文的描述,我们项目的基础结构呼之欲出:
下面我们将采用
vite
+koa
的方式创建我们的React服务端渲染项目。vite
创建项目,这里可以选择React+TS
的模板,跟着命令操作即可main.tsx
,然后按照结构创建修改文件server.js
entry-client.tsx
entry-server.tsx
index.html
为服务提供占位标记并引用entry-client.tsx
我们需要在
server.js
控制服务端渲染,这里采用koa
做为服务器,且对代码进行了开发和生产模式的区分。koa
服务器,以中间件的模式创建vite
应用,去加载对应的开发代码。koa
的应用并开启静态服务器,去加载对应生产代码。二者的思路都是,先由服务端进行静态页面渲染,再进行客户端的渲染对事件绑定等交互处理。
entry-client.tsx
和entry-server.tsx
package.json
dev
:就是我们的开发模式,只需启动服务即可。build
:这里有三个build
命令,第一个无后缀的表示执行客户端和服务端的代码打包,另外两个有后缀的是分别进行客户端和服务端的代码打包。其中--ssr
标志表明这将会是一个 SSR 构建。同时需要指定 SSR 的入口。serve
:这里有两个serve
命令,前者无后缀的表示重新打包并启动服务,后者有后缀表示启动服务,需要先手动进行打包。这部分代码较多就不贴了,可直接参考完整项目:https://github.com/wqhui/vite-react-ssr
最后,一个简单的服务端渲染的项目就搭好了,我们可以运行命令
npm run dev
查看效果。功能完善
页面404处理
上面我们虽然有处理服务器转换HTML的异常,但是没有处理访问
404
页面的情况。这里我想到的有两种方案:404
的 HTML 页面。我们在服务器获取渲染的HTML时,有传入一个
context
的属性,在React-Router V5
时可以简单的把这个属性传入到StaticRouter
中,React-Router
在匹配不到路由时会修改context.status=404
,但是在React-Router V6
已经不存在这个属性,所以我们要自己特殊处理,下面使用的是react-router-dom
中的matchRoutes
去适配。React-Router
未查找到的路由可以使用
path="*"
适配“未查找到”的路由,这里的做法很多,可查看官网,下面是用的是useRoutes
方式去适配。数据获取
客户端获取数据,一般是在组件挂载(
componentDidMount
或useEffect
)时发送请求,获取到数据后更新组件状态。因为组件挂载回调不会在服务端执行,所以不能采用客户端的获取方式,所以我们可以先获取数据,渲染组件时候传入数据,然后在转化成HTML。具体实现如下:
getInitialProps
entry-server
上处理数据获取最后,我们还需要解决服务端和客户端数据的同步问题,也就是服务端请求过的数据应该缓存起来,客户端直接使用这份缓存数据,不需要再去请求,否则界面会出现闪动。
window
上预渲染 / SSG
如果预先知道某些路由所需的路由和数据,我们可以使用与生产环境 SSR 相同的逻辑将这些路由页面预先渲染成静态 的HTML 。这也被视为一种静态站点生成(SSG)的形式。
然后在
package.json
的script
中增加如下命令,对于想要预渲染的路由界面生成静态的html。完整项目链接
https://github.com/wqhui/vite-react-ssr
参考
React服务端渲染入门 - 掘金
服务端渲染 | Vite 官方中文文档
The text was updated successfully, but these errors were encountered: