前端渲染、服务器端渲染、同构渲染

一、引

很久前,几乎所有网站都使用 ASP、Java、PHP 这类做后端渲染,但是前几年 jQuery、Angular、React、Vue 等 JS 框架的大行其道,大家转向了前端渲染。从 2014 年起又开始流行了同构渲染,号称是未来,集成了前后端渲染的优点,但转眼间三年过去了,好像还是前端渲染的天下。同构到底是不是未来?自己的项目该如何选型?我想不应该只停留在追求热门和拘泥于固定模式上,忽略了前后端渲染之“争”的“核心点”,关注如何提升“用户体验”。

二、概念

  1. 「后端渲染」指传统的 ASP、Java 或 PHP 的渲染机制;
  2. 「前端渲染」指使用 JS 来渲染页面大部分内容,代表是现在流行的 SPA 单页面应用;
  3. 「同构渲染」指前后端共用 JS,首次渲染时使用 Node.js 来直出 HTML。一般来说同构渲染是介于前后端中的共有部分。

2.1 前端渲染的优势

  1. 局部刷新。无需每次都进行完整页面请求
  2. 懒加载。如在页面初始时只加载可视区域内的数据,滚动后 rp 加载其它数据,可以通过 react-lazyload 实现
  3. 富交互。使用 JS 实现各种酷炫效果
  4. 节约服务器成本。省电省钱,JS 支持 CDN 部署,且部署极其简单,只需要服务器支持静态文件即可
  5. 天生的关注分离设计。服务器来访问数据库提供接口,JS 只关注数据获取和展现
  6. JS 一次学习,到处使用。可以用来开发 Web、Serve、Mobile、Desktop 类型的应用

2.2 后端渲染的优势

  1. 服务端渲染不需要先下载一堆 js 和 css 后才能看到页面(首屏性能)
  2. 有利于SEO
  3. 服务端渲染不用关心浏览器兼容性问题(随意浏览器发展,这个优点逐渐消失)
  4. 对于电量不给力的手机或平板,减少在客户端的电量消耗很重要

以上服务端优势其实只有首屏性能和 SEO 两点比较突出。但现在这两点也慢慢变得微不足道了。React 这类支持同构的框架已经能解决这个问题,尤其是 Next.js 让同构开发变得非常容易。还有静态站点的渲染,但这类应用本身复杂度低,很多前端框架已经能完全囊括。

三、各有优劣

可以达成共识的是,未来是前端渲染的天下,没人开始往服务器端渲染走。但是同构渲染还处于不确定的讨论中。

3.1 前端渲染

最大的问题就是 SEO、首屏性能。前端渲染常使用的 SPA 会把所有 JS 整体打包,导致的问题就是文件太大,等待很长时间。特别是网速差的时候。SEO 很好理解。由于传统的搜索引擎只会从 HTML 中抓取数据,一上来的空网页导致页面无法被抓取。

3.2同构渲染

  1. 性能
    个性化的缓存是一个问题。可以把每个用户个性化信息缓存到浏览器,这是一个天生的分布式缓存系统。我们有个数据类应用通过在浏览器合理设置缓存,双十一当天节省了 70% 的请求量。试想如果这些缓存全部放到服务器存储,需要的存储空间和计算都是很非常大。

  2. 不容忽视的服务器端和浏览器环境差异
    前端代码在编写时并没有过多的考虑后端渲染的情景,因此各种 BOM 对象和 DOM API 都是拿来即用。这从客观层面也增加了同构渲染的难度。我们主要遇到了以下几个问题:

    1. document 等对象找不到的问题
    2. DOM 计算报错的问题
    3. 前端渲染和服务端渲染内容不一致的问题

    由于前端代码使用的 window 在 node 环境是不存在的,所以要 mock window,其中最重要的是 cookie,userAgent,location。但是由于每个用户访问时是不一样的 window,那么就意味着你得每次都更新 window。 而服务端由于 js require 的 cache 机制,造成前端代码除了具体渲染部分都只会加载一遍。这时候 window 就得不到更新了。所以要引入一个合适的更新机制,比如把读取改成每次用的时候再读取。

export const isSsr = () => (
  !(typeof window !== 'undefined' && window.document && window.document.createElement && window.setTimeout)
);

原因是很多 DOM 计算在 SSR 的时候是无法进行的,涉及到 DOM 计算的的内容不可能做到 SSR 和 CSR 完全一致,这种不一致可能会带来页面的闪动。

  1. 内存溢出
    前端代码由于浏览器环境刷新一遍内存重置的天然优势,对内存溢出的风险并没有考虑充分。

  2. 异步操作
    前端可以做非常复杂的请求合并和延迟处理,但为了同构,所有这些请求都在预先拿到结果才会渲染。而往往这些请求是有很多依赖条件的,很难调和。纯 React 的方式会把这些数据以埋点的方式打到页面上,前端不再发请求,但仍然再渲染一遍来比对数据。造成的结果是流程复杂,大规模使用成本高。

  3. simple store(redux)
    这个 store 是必须以字符串形式塞到前端,所以复杂类型是无法转义成字符串的,比如 function。

总的来说,同构渲染实施难度大,不够优雅,无论在前端还是服务端,都需要额外改造。

三、总结

我们赞成客户端渲染是未来的主要方向,服务端则会专注于在数据和业务处理上的优势。但由于日趋复杂的软硬件环境和用户体验更高的追求,也不能只拘泥于完全的客户端渲染。同构渲染看似美好,但以目前的发展程度来看,在大型项目中还不具有足够的应用价值,但不妨碍部分使用来优化首屏性能。做同构之前 ,一定要考虑到浏览器和服务器的环境差异,站在更高层面考虑。

你可能感兴趣的:(前端,后端)