Live2D

前端渲染方式对比-by HZK

常见的前端渲染模式有三种,分别是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

  1. 部署成本低
  2. 页面之间跳转无需额外请求服务端,体验更好。
  1. 首屏渲染速度较慢
  2. SEO支持较差
  1. 后台项目
  2. B端项目

SSR

  1. 首屏渲染速度更快,首屏渲染性能更优。
  2. 更好的 SEO 优化
  3. 对于客户端的 JavaScript 代码的依赖较小,可以减轻首屏渲染js的执行压力。
  1. 成本较高,需要有额外的服务器和人力去维护,去完成服务的监控,扩容,容灾等。
  2. 前端编写代码时需要额外考虑服务端渲染的兼容场景。
  3. 页面跳转需要额外请求一次服务端(纯SSR项目)
  4. 代码质量要求较高,主要注重代码规范。
  1. 时效性较强的项目,比如抢购页面。
  2. 对首屏性能有更高要求的项目。
  3. 公司官网。

SSG

兼顾CSR和SSR的优势,部署成本低,首页渲染快,对SEO友好

数据时限性较差,对于需要频繁更新的场景成本反而更高。

  1. 新闻页面
  2. 文档页面
  3. 博客页面

 

SSR的一些扩展

 

上文提到了,SSR的缺点主要是成本较高,如果项目是纯SSR的MPA项目,则每次都需要额外请求一次服务端,首屏的优势反而没有了。那么如何来改善这两个问题呢?

世面上的常见做法: 首屏SSR,其余的页面跳转走csr。搭配下面的两种部署方式来降低SSR的成本。

Serverless

传统的服务端渲染(SSR)通常依赖于一台完整的服务器。为了应对高流量访问,这些服务器的配置往往较高,但这也带来了一个明显问题:资源利用不足。

流量并不是恒定的,随着用户访问高峰和低谷的变化,高配置的服务器在低谷期会浪费大量资源。Serverless应运而生,它允许你按需付费,例如,如果你的应用程序在某段时间仅有10秒的访问,你只需为这10秒付费。许多云服务商都提供了Serverless部署模式。

云厂商通过让一台服务器同时服务于多个项目,动态处理请求,从而最大限度地利用服务器资源,并根据实际使用时间动态计费和扩展。

Serverless的好处包括:

  1. 按需付费:只需为实际使用的计算时间付费,避免资源浪费。
  2. 无需管理基础设施:由云提供商管理设备和运维,无需用户自行维护。

而,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>  
	)  
}

参考引用链接:

web的未来是edge

流式SSR与react

posted @   喻佳文  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示