也许你不需要用 SSR — Jamstack

date
Aug 9, 2021
slug
jamstack
status
Published
tags
Node.js
Web
summary
有前台类业务(SSR)需求、了解 Jamstack 以及解决什么问题、实践 Jamstack
type
Post

分享主题和目标

  • 有前台类业务(SSR)需求
  • 了解 Jamstack 以及解决什么问题
  • 实践 Jamstack
 

背景

前端应用主要分为:SPA(单页应用)、MPA(多页应用),主要区别在于页面渲染方式:
notion image

SSR 优缺点

优点
由图可以看出,SSR 相当于服务端(Node.js)模拟渲染流程,用户访问请求时就返回了完整的 HTML 内容,所以经常用在两个场景:
  1. SEO 诉求,用在搜索引擎检索以及社交分享,用在前台类应用。
  1. 首屏渲染时长有要求,常用在移动端、弱网情况下。
notion image
 
缺点
SSR 依赖服务端 Node.js 运行时,同时渲染本身是 耗资源操作,所以缺点也很明显:
  1. 一定的运维、服务器成本
  1. 更长的 TTFB (首字节到达时长)
针对第一个缺点,工程上可以使用 FaaS 函数降低服务器、运维成本,但是第二个缺点,除了加缓存,很难从根本上解决。
 

假设

针对前台类 SSR 站点,用户一次访问,只使用 CDN,不需要服务器、数据库不需要提供实时的服务
notion image
如果真可以,核心问题就是 怎样将最新的数据,放到 CDN 上展示 呢?
 

什么是 Jamstack?

Jamstack 是一套技术架构,在现有的技术和平台下,为前台类应用降本增收(即 For fast and secure sites)
核心是三点:
  1. JavaScript:一般是各类 BaaS 服务,如调用评论 SDK、Auth0Firebase
  1. APIs:Headless CMS 系统/平台,可提供 HTTP 接口(RESTful)、基于 Git 的 API
  1. Markup:站点生成器,关键是页面预渲染(Pre-rendering),在 build 构建时生成完整 HTML,像 Next.js、Gatsby、VuePress、Hugo 等都支持,更多
 

Headless CMS

我们不妨先想 Headless Chrome:无头浏览器,不用打开浏览器,通过 API 操作就可能实现点击、跳转、表单填写等
Headless CMS 就是不提供前台页面展示的 内容中台,只负责内容的增删改,不涉及内容展示。(广义来讲:提供增删改,并提供 OpenAPI 的查询,都可以算成 Headless CMS)
notion image
 
(图来源于 Contentful
 

页面预渲染(Pre-rendering)

服务端渲染(SSR)是在运行时执行,那么预渲染本质上还是 SSR 技术,只不过执行时机在 构建时,将用户请求与渲染分开。
notion image
 
这样就实现了:用户看到的内容是最新的,站长的服务器/运维成本是最小的(几乎为 0)
 

怎么使用 Jamstack?

使用的关键是找齐三件套,召唤 Jamstack,这里以 issues 实时列表做 Jamstack 实践
  • J:utterances 评论 SDK(如果需要登录功能 Auth0,需要借助 FaaS)
  • A:Github issues:所有 issues 都可以通过 Open API 获取
  • M:Next.js:社区非常优秀的 React SSR 框架
 
notion image
 
和一般站点相比,主要区别在于:
  • 不同于静态站点,demo 可以对用户/爬虫展示 分钟级最新数据
  • 不同于 SSR 站点,demo 全站托管在 CDN 上(即 Github Pages),No Servers
 
notion image
 
Demo 中用到的核心有两个:构建时渲染、内容更新触发

构建时渲染

顾名思义,在构建时,调用 API 执行渲染,生成最新的页面 HTML,这里使用 Next.js 的 getStaticProps 做 数据预获取(Data Fetching) 处理:
 
// pages/index.tsx // 只在服务端执行 export const getStaticProps = async () => { const res = await fetch( 'https://api.github.com/repos/reactNoder/jamstack/issues?state=open', ); const data = await res.json(); if (!data) { return { notFound: true, }; } return { props: { data, // will be passed to the page component as props} }, revalidate: 1, }; };
 
当然,也是支持动态路由(如 /:id)生成的,借助 getStaticPaths,从 API 接口中返回所有的详情页 id
export async function getStaticPaths() { const fromServer = await fetch('https://api.foo/list'); return { paths: [ { params: { id: '1' } }, { params: { id: '2' } }, ...fromServer ], }; }

内容更新触发

简单来讲,就是内容更新后,触发站点执行构建并发布到 CDN 上,这里借助 Github Action 的 issue trigger,一旦 issue 变更,则执行对应操作,代码如下:
 
# .github/workflows/deploy.yml name: Deploy on: # 监听主干代码变更(样式/逻辑修改) push: branches: [ master, main ] # issue 创建/修改/删除 变更 issues: types: [opened, edited, deleted] jobs: autoDeploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: fetch-depth: '1' - uses: actions/cache@v2 id: cache with: path: | node_modules */*/node_modules key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: install if: steps.cache.outputs.cache-hit != 'true' run: yarn install --ignore-engines # 执行构建 - name: build run: | yarn build yarn export # 产物上传到 Github Pages - name: deploy uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./out
 
站点的更新流程如动图所示
notion image
 
 
当然,Jamstack 除了用的前台页面更新外,同时也可以用查询类 API 接口,将 JSON 数据放 CDN 上,动态更新 JSON 数据。例如 太阳系开放数据 API,它可以返回所有行星、卫星、矮星、彗星和小行星等各种数据。
所以,Jamstack 是将可读资源服务成本降至 0。

存在的问题

Jamstack 实际上也有一些问题:
  1. 首当其冲的是 安全生产,自动部署到生产本身具备很高风险性,失败后怎么处理?
一般的做法是在部署前,跑测试用例,✅ 通过后才能部署,确实是自动化 CD 的保障手段,但还远远不够。
  1. 应用面较小,大部分是面向千人一面的前台应用
  1. BaaS 服务建设道路漫长,高度定制化的需求往往处理不了
  1. 对开发者心智过高,devbuild 并不是完全一样,dev 正常可能部署后服务挂了(跨域请求 API 服务)

思考与展望

Jamstack 是 Serverless 中一种不错的实践,最大的优势是降低了开发和运维成本。原来建一个 Blog/CMS,需要买服务器,现在只需要 CDN + FaaS,应用运行成本大大降低!
Jamstack 目前在公司/团队内部的实践过少,也没有工程化的解决方案。
Netlify 提出的 Jamstack 抽象层(Abstraction Layer) ,通过事件驱动串联整个流程,自由组合 CMS、BaaS、Markup,形成 Jamstack 平台 Nacelle
notion image
想想前后端逐渐融合在一起,通过 CDN + FaaS + BaaS 就能单独开发一个前台产品,后台操作下前台就更新了,运维成本为 0,服务按次/量收费,计算资源更加合理分配和使用,不香吗?
notion image

参考


© ycjcl868 2021 - 2024