2021 / 07 / 29

SSR项目规划

本文字数: 3910阅读时间: 9分钟

目前公司项目的类型多数是SPA,前端负责界面显示,后端负责数据存储和计算,各司其职,不会把前后端的逻辑混杂在一起,可以减轻服务器压力,服务器只用提供调用的接口,不用管展示逻辑和页面合成,吞吐能力会提高几倍;开发非常方便但是不可避免的有副作用:

  • 首屏加载速度慢
  • SEO 支持缺失

所以现在准备规划一种方案可以兼顾SPA和服务器渲染的优势

SSR 介绍

SSR(Server Side Rendering)刚好弥补了SPA的不足,使得前后端分离真正可以达到和传统网站的效果并超越传统网站。 关于前后端分离或SPA的优点可以自行了解,SSR相对于SPA主要有以下提升

更快的首屏展示速度

由于 SPA 需要先加载完 JavaScript 脚本才能开始渲染,所以首屏渲染并不快,在 JavaScript 加载中只能显示简单的等待画面。 SSR 是服务端渲染,可以直接输出 HTML 内容,在首屏展示上更有优势。

SEO 支持

SEO 的重要性大家都知道,因为 SEO 需求导致 SPA 对电商,首页,内容类网站等需要 SEO 的页面望而却步,只能回到传统服务端渲染的方案中。 SSR 弥补了 SEO 的不足,真正的可以通过前后端分离的方式完成此类网站。

框架选型

SSR目前分Client(客户端)和Server(服务端),以及可能配合PWA,主要使用的是以下:

Client

  • Vue,Vuex,Vue-Router (Vue全家桶)
  • Vue-Meta(完善SEO配置)
  • Nuxt(SSR UI渲染,同时抽象出客户端/服务器分布。)

Server

  • koa (Nodejs 下一代的Web框架)
  • logger (日志)
  • Routing-controllers (使用方法创建控制器类作为处理请求的操作,将路由控制器与koa一起使用)
  • Sequelize (Sequelize是MySQL的基于承诺的Node.js ORM框架。它具有可靠的事务支持,关系,读取复制等功能。)
  • Redis (缓存)

PWA (Progressive Web Apps)

  • Workbox (Google 开发的工具集,能够方便地集成到 Webpack、Gulp 等构建流程中,帮助开发者生成或者注入部分 ServiceWorker 相关代码)
  • Service Worker (服务工作线程, 浏览器后台独立于网页的脚本,预缓存,消息推送,后台同步)
  • App Shell模型 (加载必要界面, 进行离线缓存)

目标效果

  • 直接访问页面达到传统服务器渲染的速度
  • 项目内跳转像 SPA 一样快速跳转
  • 支持 SEO
  • 同构代码,节省开发工作量,提升效率
  • 前后端分离,可并行开发,独立管理

核心思路

  • 输入地址访问或刷新访问时在 Client 端渲染页面
  • 浏览器中跳转时在 Server 端渲染页面
  • 前后端公用同一套业务代码
  • 利用中间件,适配器以及环境判断屏蔽掉业务代码中的环境差异

目录结构

├─client
│  ├─.nuxt
│  │  ├─components
│  │  └─views
│  ├─assets
│  │  ├─icon
│  │  └─less
│  ├─biz-components
│  ├─components
│  ├─layouts
│  ├─middleware
│  ├─pages
│  ├─plugins
│  ├─service
│  ├─static
│  │  └─base
│  ├─store
│  │  └─modules
│  └─utils
├─server
│   ├─controllers
│   │  └─api
│   ├─interfaces
│   ├─middlewares
│   └─utils
├─.editorconfig
├─.eslintignore
├─.eslintrc.js
├─index.js
├─nuxt.config.js
├─package.json
├─package-lock.json
├─README.md
├─tsconfig.json
├─tslint.json
└─yarn.lock

总体规划

使用Nuxt和PWA配合Koa完成SSR,利于SEO优化和首屏渲染时间加速.但是也有局限性存在: * 服务端压力较大: 在高并发的访问下,服务端大量占用服务器的资源 * 开发条件受限: created和beforeCreate之外的生命周期钩子不可用,因此项目引用的第三方的库也不可用其它生命周期钩子,这对引用库的选择产生了很大的限制; * 学习成本相对较高: 除了对webpack、Vue要熟悉,还需要掌握node、Koa相关技术。相对于客户端渲染,项目构建、部署过程更加复杂。

示意图

build示意图[ build示意图 ]

客戶端规划

在VUE的項目中,同一套代码在 Node 端和浏览器端都能运行,是没有“后端模板”的概念的,同样对于“内容片段”也是如此。在首屏使用服务端渲染直出 HTML 后,浏览器端进行 Hydrate(混合),绑定事件使页面真正可响应,除非刷新页面,否则后续的路由切换都将由前端路由器完成。 在 Vue SSR 项目中的实际效果就是首屏由服务端渲染,随后当路由切换时,客户端会请求对应的路由组件代码。所以前端路由向服务端请求内容片段渲染内容实际上已经完成了,对于开发者并没有额外的工作要做。

  • 引用Nuxt 可选引用PWA
  • 预缓存 App Shell 页面
  • Service Worker 拦截所有 HTML 请求,统一返回缓存的 App Shell 页面。同时向服务端请求当前页面需要的内容片段并写入缓存
  • 前端路由( app.js )向服务端请求内容片段,发现缓存中已存在,将其填充进 App Shell 中,完成前端渲染
  • 离线用户体验(选用)

SSR[ SSR ]

服務端规划

这里的服务端是指Nuxt和渲染客户端使用的,因为公司用的后端是Java,所以这里的Node作为中间层使用,并且完成前端使用SSR,页面缓存也可以帮助前端调整一下后端的数据,或者写接口,拓展性非常强。

  • 支持返回完整页面和内容片段( contentOnly )
  • 渲染Nuxt生成的UI
  • 页面缓存

SSR需要先在服务器端生成虚拟 DOM ,然后再序列化为 HTML 文本,而且为了请求之间的隔离性,每次请求都会创建一个新的 context ,这种做法使得它与直接使用模板引擎相比性能差了数十倍。
所以为了提高并发,并且为了降低服务器的性能损耗,所以需要要针对常用界面或者特定组件做长时间的缓存,这里用Redis做缓存,流程图如下

服务端渲染时序图-命中缓存[ 服务端渲染时序图-命中缓存 ]

服务端渲染时序图-未命中缓存[ 服务端渲染时序图-未命中缓存 ]

服務器规划

  • cdn缓存策略
  • nginx缓存策略

参考资料

App Shell 模型
offline
workbox
在 Vue SSR 中使用 Service Worker
SSR 架构项目实现离线可用
VUE SSR定制与使用心得
解密Vue SSR

总结

目前可用Nuxt,vue-meta配合Koa实现SEO和首屏渲染的时间. 如果剥离Vue全家桶实现SSR且与PWA的Workbox实现更好的优化和用户体验,以及程序良好的性能,需要较高的开发成本且对开发人员有较高的需求 SSR与PWA的配合使用,实际运作生产环境开发难度较高, CPU 是 SSR 的性能瓶颈

更多阅读