常见的前端渲染模式有三种,分别是csr/ssr/ssg
CSR
客户端渲染 Client-Side Rendering
页面的渲染全部由浏览器完成,当用户访问一个页面链接时,服务端相应对应的html文件,下载完js、css图片等静态资源完成渲染。
一般的html只会有基本的骨架,类似如下的结构。所有的跳转逻辑,资源的加载逻辑,都由js去完成。
这种渲染模式的优点就是不需要额外起一个pod服务,只需要把打包的html,js,css等静态产物丢到cdn,即可实现服务上线+跨区域加速访问。
缺点是会降低首屏渲染速度,因为浏览器需要等待 JavaScript 代码的下载和执行完成后才能开始渲染页面。
在页面加载过程中,还依赖接口的请求,只有接口请求成功后才能渲染出必要的页面,在请求过程中会有相对较长的loading页面状态。
此外,CSR 对于 SEO 的支持较弱,因为搜索引擎爬虫访问的时候会只拿到基本的骨架,可能页面未渲染完就退出了页面,因此对网页收录和排名都有一定的影响。
比较试用这次渲染模式的项目,一般是B端的后台类项目,不太关注首屏性能,也不需要seo收录。
也比较适合一些对成本控制要求有一定要求的项目,以较小的成本上线一个项目。
比较成熟的技术方案:creat-react-app
SSR
服务器渲染(Server-Slide Rendering)
页面的首屏内容是由服务端完成,客户端拿到页面内容就是带首屏数据的页面,客户端只需要进行简单的渲染并绑定事件即可。没有了中间的loading态,白屏时间也会更好。并且服务端天生就有离数据更近的优势,因此在接口响应上也会比csr要快不少。
不仅在首屏性能上,SSR 对于搜索引擎的爬虫也更加友好,可以更容易地被搜索引擎收录和排名。对SEO有要求,对首屏渲染速度有要求的项目。
缺点也很明显:依赖额外的服务器来承载这个pod服务。当流量较大或者突然激增的场景,要考虑容器的动态扩容,服务监控,检查重启等功能。一般需要由专门的运维部门来维护这个项目的正常运行。成本比csr要高出不少。同时,SSR对前端开发的代码质量和规范上也有一定的要求。因为一套代码得同时运行在服务端和客户端,因此需要注意不能在公共部分写一些client的代码或者server的代码。比如在客户端引用了node的fs模块,在server端执行了windows的一些api比如,document.getElementById,localStorage等。同时为了保证在客户端注水成功,必须保证server端的代码执行结果和client端一致,不能出现类似new Date().getTime() ,或者Math.random()这种随机性很强的代码,他必然会导致两端结果不一致导致注水失败,而强制重新渲染一次页面,页面性能或大幅下降。
比较成熟的技术方案:Next.js/umi.js/Ice
SSG
静态站点生成(Static Site Generation)
如果一个网页的内容不会经常变更,又想要满足首屏性能和SEO的需求,那SSG静态站点生成就非常合适了。比如一个文档或者新闻站点,对时效性不强,可能一周就变更一次,那我们就可以在打包html的时候,额外去请求一次接口,并且带着这个接口数据完成页面的渲染。
SSG的缺点就相对明显了,每次请求的html内容都是固定的。每次数据变更都需要重新打包这个html。或者走js去异步更新数据。
优点 |
缺点 |
比较适合的项目 |
|
CSR |
|
|
|
SSR |
|
|
|
SSG |
兼顾CSR和SSR的优势,部署成本低,首页渲染快,对SEO友好 |
数据时限性较差,对于需要频繁更新的场景成本反而更高。 |
|
SSR的一些扩展
上文提到了,SSR的缺点主要是成本较高,如果项目是纯SSR的MPA项目,则每次都需要额外请求一次服务端,首屏的优势反而没有了。那么如何来改善这两个问题呢?
世面上的常见做法: 首屏SSR,其余的页面跳转走csr。搭配下面的两种部署方式来降低SSR的成本。
Serverless
传统的服务端渲染(SSR)通常依赖于一台完整的服务器。为了应对高流量访问,这些服务器的配置往往较高,但这也带来了一个明显问题:资源利用不足。
流量并不是恒定的,随着用户访问高峰和低谷的变化,高配置的服务器在低谷期会浪费大量资源。Serverless应运而生,它允许你按需付费,例如,如果你的应用程序在某段时间仅有10秒的访问,你只需为这10秒付费。许多云服务商都提供了Serverless部署模式。
云厂商通过让一台服务器同时服务于多个项目,动态处理请求,从而最大限度地利用服务器资源,并根据实际使用时间动态计费和扩展。
Serverless的好处包括:
- 按需付费:只需为实际使用的计算时间付费,避免资源浪费。
- 无需管理基础设施:由云提供商管理设备和运维,无需用户自行维护。
然而,Serverless也存在缺点,例如,可能会遇到冷启动问题。在首次或长时间未使用后再次启动时,可能会导致请求延迟。
Edge Function
CDN只能处理静态资源,Serverless能实现服务的动态扩容,那有没有什么办法能把两者结合呢。
这里就引入了Edge function(边缘计算)
其模式就相当与把你的项目部署在多台服务端,和cdn的原理类似,根据用户的ip来判断哪台机器离用户更近,就优先分配到那台机器。同时,他们会在全国各地专门部署机器用来处理数据的处理和计算。
在边缘计算中,比如每个SSR服务都需要预先请求接口数据,然后渲染html页面,那这个“通用”的功能就可以单独抽象出来,用单独的服务端来执行这次函数,它在用户附近的节点上运行,从而提升数据获取和页面生成效率。
比较成熟的就是vercel公司推出的Next.js,通过Edge Function实现了高效部署。将项目部署在Vercel上,并在Next.js中编写特定的Edge Function代码,既满足了近距离资源加载需求,也实现了去中心化的服务部署。
通过对比可见,CDN主要加速静态资源访问,而Edge Function能够在“边缘”处理动态计算,为用户提供更快的服务响应。
SSR框架的渲染原理
打包阶段
约定式路由
SSR一般都为MPA项目,以src/pages/xxx.[js|ts|jsx|tsx]为资源入口。
遍历这些资源入口,作为打包器的入口(entry),打包器可以是vite,webpack,rspack,esbuild等等。
以webpack为例,别分以target:node和web两份标准去打出两份不同的产物。
同时,webpack会帮我们生成一个buildmanifest.json 里面简单的记录我们这个路由下生成了哪些js和css产物
部署阶段
以koa为例,简单的起一个http-server服务器,用来处理后续的请求。
请求阶段
流式SSR渲染
和传统的ssr渲染不同,流式ssr允许分块渲染,分块注水。
对比gif图
流式渲染
普通渲染
基本原理
依赖Transfer-Encoding,把数据被拆成一系列的chunk返回。
const http = require('http');
const server = http.createServer(async (req, res) => {
res.setHeader('Content-Type', 'text/html');
res.setHeader('Transfer-Encoding', 'chunked')
res.write('<html>');
res.write('<head><title>Stream Demo</title><head>');
res.write('<body>');
await new Promise((resolve, reject) => setTimeout(resolve, 2000));
//第一个http chunk
res.write('<h2>first content</h2>');
await new Promise((resolve, reject) => setTimeout(resolve, 2000));
//第二个http chunk
res.write('<h2>second content</h2>');
res.write('</body></html>');
res.end();
});
server.listen(8080);
把之前的renderToString api替换成-> renderToNodeStream,renderToPipeableStream,
结合React18的Suspense+lazy把代码分块打包。
import { Suspense, lazy } from 'react'
const OtherComponent = lazy(() => import('./OtherComponent'))
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
)
}
参考引用链接:
标签:渲染,前端,HZK,首屏,js,SSR,服务端,页面 From: https://www.cnblogs.com/yujiawen/p/18589075