乐趣区

关于ssr:新一代Web技术栈的演进SSRSSGISRDPR都在做什么

在之前的一篇文章里,初步介绍了 Jamstack 这套建站技术栈的背景以及各方面优劣势。

所以这次这篇文章会更加深刻,聊聊这套技术栈的演进以及业界的一些最佳实际。

在开始浏览之前,先解释一下文章里用到的英文缩写:

  • CSR:Client Side Rendering,客户端(通常是浏览器)渲染;
  • SSR:Server Side Rendering,服务端渲染;
  • SSG:Static Site Generation,动态网站生成;
  • ISR:Incremental Site Rendering,增量式的网站渲染;
  • DPR:Distributed Persistent Rendering,分布式的继续渲染。

从 SSR 到 SSG

SSR 这套技术栈置信很多人应该都十分相熟了(如果你不相熟的话能够先浏览相干文章),React/Vue/Angular 等等都从框架层面间接提供了反对,例如在 React 中你能够这样应用:

import React from 'react'import ReactDOMServer from 'react-dom/server'
const html = ReactDOMServer.render(<h1>Hello, world!</h1>)

SSR 最早是为了解决单页利用(SPA)产生的 SEO、首屏渲染工夫等问题而诞生的,在服务端间接实时同构渲染用户看到的页面,能最大水平上进步用户的体验,流程相似上面:

但 SSR 引入了另一个问题,既然要做服务端渲染,就必然 须要一个实时在线的后盾服务(通常是基于 Node.js 的服务)用来承载页面申请,那么:

  1. 须要服务器的计算资源和公网流量 来部署这套服务,并且耗费的资源与页面的访问量成正相干,当页面的访问量突增时,渲染服务也须要进行扩容;
  2. 服务端只能部署在无限的几个地区,对于间隔服务端较远的用户而言,加载速度跟动态资源的 CDN 相比,慢了一个数量级(通常是 1-5ms VS 50-100+ms);
  3. 日常也存在传统服务端同样的运维、监控告警等方面的累赘,团队 须要额定的人力来开发和保护

有没有方法解决这些问题呢?

咱们从新对 SSR 进行扫视,服务端渲染出的页面,逻辑上讲能够分成上面两大块:

  1. 变动不频繁,甚至不会变动的内容:例如文章、排行榜、商品信息、举荐列表等等,这些数据非常适合缓存;
  2. 变动比拟频繁,或者千人千面的内容:例如用户头像、Timeline、登录状态、实时评论等。

例如,在一篇文章的页面中,文章的主题内容是偏差于动态的,很少有改变,那么每次用户的页面申请,都通过服务端来渲染就变得十分不值得,因为每次服务端渲染进去大部分内容都是一样的

咱们齐全能够将文章的页面渲染为动态页面,至于页面内那些动静的内容(用户头像、评论框等),就通过 HTTP API 的模式进行浏览器端渲染(CSR):

这样做有很多益处:

  1. 因为文章内容曾经被动态化了,所以它是 SEO 敌对的,能被搜索引擎轻松爬取;
  2. 大大加重了服务端渲染的资源累赘,不须要额定做一套 Node.js 服务;
  3. 用户始终通过 CDN 加载页面核心内容,CDN 的边缘节点有缓存,速度极快;
  4. 通过 HTTP API + CSR,页面内主要的动静内容也能够被很好地渲染;
  5. 数据有变动时,从新触发一次网站的异步渲染,而后推送新的内容到 CDN 即可。
  6. 因为每次都是全站渲染,所以网站的版本能够很好的与 Git 的版本对应上,甚至能够做到原子化公布和回滚。

这便是 Gatsby.js、Next.js 这样的网站生成器解决的问题,他们属于 React/Vue 更上一层的框架(Meta Framework),通过 SSR 把动态化的 Web 利用渲染为多个动态页面,并且对高度动静的内容也保留了 CSR 的能力。

从 SSG 到 ISR/DPR

仔细的同学肯定发现了 SSG 这样的模式,看似美妙,但存在一个瑕疵:

对于只有几十个页面的集体博客、小型文档站而言,数据有变动时,跑一次全页面渲染的耗费是能够承受的。

但对于百万级、千万级、亿级页面的大型网站而言,一旦有数据改变,要进行一次全副页面的渲染,须要的工夫可能是按小时甚至按天计的,这是不可承受的。

为了解决这个问题,各种框架和动态网站托管平台都提供了不同的计划,这里咱们介绍 ISR 和 DPR 两种。

ISR:

Incremental Site Rendering

既然全量预渲染整个网站是不事实的,那么咱们能够做一个切分:

  1. 关键性的页面(如网站首页、热点数据等)预渲染为动态页面,缓存至 CDN,保障最佳的拜访性能;
  2. 非关键性的页面(如流量很少的老旧内容)先响应 fallback 内容,而后浏览器渲染(CSR)为理论数据;同时对页面进行异步预渲染,之后缓存至 CDN,晋升后续用户拜访的性能。

页面的更新遵循 stale-while-revalidate 的逻辑,即始终返回 CDN 的缓存数据(无论是否过期);如果数据曾经过期,那么触发异步的预渲染,异步更新 CDN 的缓存。

这就是增量式更新(ISR)的概念,这个概念最早由 Next.js 在 9.5 版本中提出,上面是一个小 Demo:

Static Reactions Demo: https://reactions-demo.vercel.app/

在 Next.js 中,你能够应用 getStaticPaths() 来定义哪些门路须要预渲染,通过 getStaticProps() 来获取预渲染须要的数据:

// 定义哪些页面须要预渲染
export async function getStaticPaths() {
return {
// 只有 /posts/1 和 /posts/2 会被预渲染
paths: [{params: { id: '1'} }, {params: { id: '2'} }],
// 其它页面,如 /posts/3,都会返回 fallback 页面,而后 CSR
fallback: true,
}
}

// 定义预渲染须要的数据
export async function getStaticProps({params}) {
// 拉取对应的文章内容
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()

return {props: { post},
revalidate: 60 // 数据有效期为 60 秒
}
}

但 ISR 存在局部缺点:

  1. 对于没有预渲染的页面,用户首次拜访将会看到一个 fallback 页面,此时服务端才开始渲染页面,直到渲染结束。这就导致用户 体验上的不统一
  2. 对于曾经被预渲染的页面,用户间接从 CDN 加载,但这些页面可能是曾经过期 的,甚至过期很久的 ,只有在用户刷新一次,第二次拜访之后,能力看到新的数据。对于电商这样的场景而言,是不可承受的(比方商品曾经卖完了,但用户看到的过期数据上显示还有)。
    具体对于 ISR 的利弊,能够进一步看 Netlify 的这篇文章:

Incremental Static Regeneration: Its Benefits and Its Flaws:https://www.netlify.com/blog/2021/03/08/incremental-static-regeneration-its-benefits-and-its-flaws/

DPR:

Distributed Persistent Rendering

为了解决 ISR 的一系列问题,Netlify 在前段时间发动了一个新的提案:

Distributed Persistent Rendering (DPR):https://github.com/jamstack/jamstack.org/discussions/549

DPR 实质上讲,是对 ISR 的模型做了几处改变,并且搭配上 CDN 的能力:

  1. 去除了 fallback 行为,而是间接用 On-demand Builder(按需构建器)来响应未通过预渲染的页面,而后将后果缓存至 CDN;
  2. 数据页面过期时,不再响应过期的缓存页面,而是 CDN 回源到 Builder 上,渲染出最新的数据;
  3. 每次公布新版本时,主动革除 CDN 的缓存数据。

在 Netlify 平台上,你能够像这样定义一个 Builder,用于预渲染或者实时渲染。这个 Builder 将会以 Serverless 云函数的形式在平台上运行:

const {builder} = require("@netlify/functions")

async function handler(event, context) {

return {
statusCode: 200,
headers: {"Content-Type": "text/html",},
body: `<!DOCTYPE html> <html> <body> Hello World </body> </html>`,
};
}

exports.handler = builder(handler);

更多详细信息能够参考文档:https://docs.netlify.com/configure-builds/on-demand-builders/

当然 DPR 还在很初期的阶段,就目前的探讨来看,仍然有一些问题:

  • 新页面的拜访可能会触发 On-demand Builder 同步渲染,导致当次申请的响应工夫比拟长;
  • 比拟难进攻 DoS 攻打,因为攻击者可能会大量拜访新页面,导致 Builder 被大量并行地运行,这里须要平台方实现 Builder 的归一化和串行运行。

总结

Jamstack 这套技术栈在国外的风行,很大水平上得益于近年来相干云服务和云平台的成熟:

  • 新一代的 CDN 技术,包含更高级、更精密的缓存控制能力;
  • Serverless 状态的计算服务(如云开发 CloudBase 提供的云函数与云托管性能),让 SSR 和 SSG 免于服务器运维的苦恼,开发者只须要重点关注前台逻辑;
  • 越来越丰盛的 BaaS 提供方,提供了包含数据存储、鉴权、电商、CMS、音视频、AI 等等“中台化”的能力,开发者只须要组合这些 BaaS 服务,专一于本身的业务逻辑即可。

Jamstack 非常适合以出现内容为主的网站,如文档、博客、电商网站、论坛、官网等等,所以更多地应该将它视为“建站技术”,是目前诸多建站技术栈(LAMP、MEAN 等等)的一个新生替代品。极低的运维老本、Serverless、疾速、平安、且不损失网站的动态性,是它的外围劣势。

当然它自身并不是完满的,SSG、ISR、DPR 这些解决方案,都或多或少有一些瑕疵和问题,它们实质上就是在均衡动态性、渲染性能、缓存性能这三个矛盾点,仍然须要持续摸索和演进上来。

另外,除了上文提到的 Netlify 和 Vercel 这两家小而美的平台以外,国外的几家大型云厂商(GCP、AWS、Azure)也提供了相似的产品,向 Web 前端开发者提供对 Jamsatck 等新生代技术栈的反对:

  • Firebase Hosting
  • Azure Static Web Apps
  • AWS Amplify

国内市场上,这块产品目前还处于缺位的状态,尽管底层的 IaaS 能力(对象存储、CDN、Serverless、网关等等)都趋近于欠缺,但还短少可能把这些能力组合封装起来的一层,前段时间我也表白过相似的想法:
“常常看到有人探讨为啥国内没有 netlify、vercel 这样的 web 托管产品,其实在国内要做个相似的货色还真没那么容易,起码要买通 CDN、对象存储、Serverless、API 网关,正式产品化的话还要思考计费、平安、用户隔离等等,能残缺提供这套基建的国内厂商就 AT 两家,并且边边角角上还缺了挺多能力的。”

当然除了技术层面的起因外,国内外的市场、网络环境、技术生态都是齐全不同的,仅仅是“Copy to China”的形式很可能会导致产品水土不服,不过这就超出本文的领域了,能够后续安顿一篇文章具体聊聊。

产品介绍

云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为开发者提供高可用、主动弹性扩缩的后端云服务,蕴含计算、存储、托管等 serverless 化能力,可用于云端一体化开发多种端利用(小程序,公众号,Web 利用,Flutter 客户端等),帮忙开发者对立构建和治理后端服务和云资源,防止了利用开发过程中繁琐的服务器搭建及运维,开发者能够专一于业务逻辑的实现,开发门槛更低,效率更高。
开明云开发:https://console.cloud.tencent.com/tcb?tdl_anchor=techsite
产品文档:https://cloud.tencent.com/product/tcb?from=12763
技术文档:https://cloudbase.net?from=10004
技术交换群、最新资讯关注微信公众号【腾讯云云开发】

退出移动版