关注「前端向后」微信公众号,你将收获一系列「用心原创」的高质量技术文章,主题包括但不限于前端、Node.js以及服务端技术html
一.*** 简介前端
***(Server-Side Rendering)并非什么新奇的概念,先后端分层以前很长的一段时间里都是以服务端渲染为主(JSP、PHP),在服务端生成完整的 HTML 页面
摘自图解 *** 等 6 种前端渲染模式后端
之因此要在服务端完成组件渲染工做,是由于有性能与可访问性两大优点浏览器
二.2 大优点
性能
与 CSR(Client-side rendering)模式相比,*** 的性能优点体如今 2 方面:缓存
网络链路服务器
省去了客户端二次请求数据的网络传输开销微信
服务端的网络环境要优于客户端,内部服务器之间通讯路径也更短cookie
内容呈现网络
首屏加载时间(FCP)更快并发
网络链路上,由服务端发出接口请求,将返回数据随 HTML 响应内容一次性传递到客户端,比 CSR 二次请求更快。而且服务端网络传输速度更快(能够有更大带宽)、通讯路径更短(能够同机房部署)、通讯效率也更高(能够走 RPC)
内容呈现方面,CSR 的 HTML 大可能是个空壳儿:
<!DOCTYPE html> <html> <head> <title>My Awesome Web App</title> <meta charset="utf-8"> </head> <body> <div id="app"></div> <script src="bundle.js"></script> </body> </html>
客户端拿到这种 HTML 只能当即渲染出一页空白,二次请求的数据回来以后才能呈现出有意义的内容,而 *** 返回的 HTML 是有内容(数据)的,客户端可以马上渲染出有意义的首屏内容(First Contentful Paint)。同时,静态的 HTML 文档让流式文档解析(streaming document parsing)等浏览器优化机制也能发挥其做用
关键区别是 *** 不依赖客户端环境,包括网络环境和设备性能,即便用户的网络状况很糟(弱网)、设备性能不好(廉价、老旧设备),服务端渲染一样可以保障与最优用户环境(Wi-Fi 网络、高端设备)下相近的内容加载体验
可访问性
可访问性(accessibility)从两方面理解:
对人:古老、特殊的用户设备,好比禁用了 JavaScript
前者通常没必要太过在乎,后者要关注两大“客户”:
搜索引擎:SEO
对 PC 站点而言,保证搜索引擎可以正确索引、准确理解页面内容,有重要的商业价值(搜索结果靠前,曝光量更大)。移动端虽没必要考虑搜索引擎爬取,但也有相似的社交分享需求,社交媒体会抓取目标页面中的图片等做为缩略信息
P.S.诚然,有些搜索引擎可以正确爬取重 CSR 的 SPA,但不是所有,而且一大批社交媒体大都只从响应 HTML 中提取部份内容做为缩略信息,动态渲染 HTML(部分)内容的需求真切存在
虽具备这些优点,但 *** 却远不如 CSR 应用普遍,是由于 *** 面临着 6 大难题
三.6 个难题
难题 1:如何利用存量 CSR 代码实现同构
为了降级、复用、下降迁移成本等目的,一般会采用一套 JavaScript 代码跨客户端、服务端运行的同构方式来实现 ***,然而,要让现有的 CSR 代码在服务端跑起来,先要解决诸多问题,例如:
客户端依赖:分为 API 依赖和数据依赖两种,好比window/document之类的 JS API、设备相关数据信息(屏幕宽高、字体大小等)
生命周期差别:例如 React 中,componentDidMount在服务端不执行
异步操做不执行:服务端组件渲染过程是同步的,setTimeout、Promise之类的都等不了
两边共享状态:每一份须要共享的状态都要考虑(服务端)如何传递、(客户端)如何接收
难题 2:服务的稳定性和性能要求
与客户端程序相比,服务端程序对稳定性和性能的要求严苛得多,例如:
稳定性:异常崩溃、死循环
所以面临后端专业性问题,Demo 级的 *** 可能并不难,但高可用的 *** 服务却绝非易事,如何应对大流量/高并发,如何识别故障,如何降级/快速恢复,哪些环节须要加缓存,缓存如何更新……
难题 3:配套设施的建设
*** 最核心的部分是渲染服务,但除此以外还要考虑:
本地开发套件(校验 + 构建 + 预览/HMR + 调试)
一整套的工程设施,在 *** 模式下都须要从新考虑
难题 4:钱的问题
引入 *** 渲染服务,其实是在网络结构上加了一层节点,而大流量所过之处,每一层都是钱:
Most importantly, *** React apps cost a lot more in terms of resources since you need to keep a Node server up and running.
将组件渲染逻辑从客户端改到服务器执行,计算资源的成本必须考虑在内
难题 5:hydration 的性能损耗
客户端接到 *** 响应以后,为了支持(基于 JavaScript 的)交互功能,仍然须要建立出组件树,与 *** 渲染的 HTML 关联起来,并绑定相关的 DOM 事件,让页面变得可交互,这个过程称为 hydration
hydration 所需加载、执行的 JavaScript 代码不见得比 CSR 模式少多少,这部分工做在客户端执行,受限于用户设备的性能,在较差的设备下可能会形成可感知的不可交互时间:
CSR:可交互可是没有数据(还在异步请求数据,可能会持续很长)
富交互的场景下,后者不必定比前者用户体验更好
难题 6:数据请求
服务端同步渲染要求先发请求,拿到数据后才开始渲染组件,那么面临 3 个问题:
数据依赖要从业务组件中剥离出来
缺失客户端公参(包括 cookie 等客户端会默认带上的 header 信息)
目前主流的 CSR 模式下,数据依赖与业务组件存在紧耦合,要由服务端发起的数据请求全都掺杂在组件生命周期函数中,剥离数据依赖意味着须要同时改造 CSR 代码。公参、数据协议等差别对代码复用、可维护性也提出了一些新的挑战
四.应用场景
不管首屏加载性能仍是可访问性,都是对内容密集型页面才有意义,而对于交互密集型的页面,*** 所能提早渲染的内容很少,对用户意义不大,SEO 的必要性也值得商榷。所以,*** 适用于偏静态的内容展现场景,典型的,商品详情、攻略、文章等图文混排的场景
另外一方面,不必定非要 100% ***,渲染特定页面,甚至只渲染个页面框架也是不错的应用:
“Application Shell” is an excellent concept. But sometimes, we might need to render a part of the page in the server. It could be the header with user info. In such cases, you need server-side rendering.
参考资料
A PAIN IN THE REACT: CHALLENGES BEHIND ***
Why it’s tricky to measure Server-side Rendering performance:angular *** 与 CSR 对照实测,数据可参考