前端同构渲染的相关架构,给我最直观的感觉,这是前端渲染最为复杂的一种方案,也是为了追求极致的用户体验不得不去作的一种尝试,虽然 Node.js 的引入赋能了传统前端领域、SEO 优化也再也不是个问题,但很明显,这些只是副产品。html
上帝为了咱们开了一扇窗,同时也会为咱们关上一扇门。前端
咱们所知的传统型 SPA,单页面应用,贴近用户端越近,交互越复杂,它的弊端就越明显,在咱们享受 JavaScirpt 给咱们带来的无刷新体验和组件化带来的开发效率的同时,『白屏』这个随着 SPA 各类优势随之而来的缺点被遗忘,咱们拥有菊花方案在 JavaScript 没有将 DOM 构建好以前蒙层,拥有白屏监控方案将真实用户数据上报改进,但并无触碰到白屏问题的本质,那就是『DOM 的构建者是 JavaScript,而非原生的浏览器』。vue
<html> <head><title /></head> <body> <div id="root"></div> <script src="render.js"></script> </body> </html> 复制代码
如上代码,在 SPA 架构中,服务器端直接给出形如这样的 HTML,浏览器在渲染 body#root 这个节点完成以后,页面的绘制区域其实仍是空的,直到 render.js 构建好真实的 DOM 结构以后再 append 到 #root上去。此时,首屏展现出来时,必然是 render.js 经过网络请求完毕,而后加上 JavaScript 执行完成以后的。node
让咱们回到最初的那个前端时代,那时候 JavaScript 尚未那么强大,咱们的服务器端所有吐出 HTML 给前端,咱们使用 jQuery 解决用户的交互,这种方式虽有不少弊病,但不能否认的是拥有理论上最低白屏时间。webpack
<html> <head><title /></head> <body> <div id="root"> <div class="header"> <img src="logo.png" /> </div> <div calss="content"> <div class="shopitem"> </div> </div> </div> </body> </html> 复制代码
如上代码,在直出的服务器渲染中,浏览器直接拿到最终的 HTML,浏览器经过解析 HTML 以后将 DOM 元素生成而进行渲染。因此相比于 SPA,服务器端渲染从直观上看:ios
**web
Node.js 的出现极大程度的给传统前端赋予了更大的能量,前端的分离也从前期的物理文件的区分转变为职责上的区分,前端开发者从页面仔的噩梦中解脱出来,最重要的是,JavaScript 能在服务器端执行了。在享受这些红利的同时,咱们就会不自觉的设想一种方案,它拥有 SPA 的大部分优势,却解决了它大部分的缺点,那就是服务器端输出 HTML,而后由客户端复用该 HTML,继续 SPA 模式,这样岂不是既解决了白屏和 SEO 问题,又继承了无刷新的用户体验和开发的组件化嘛。redis
嗯,若是这样的话,就会有个一致性的问题。咱们必须在浏览器端复用服务器端输出的 HTML 才能避免多套代码的适配,而传统的模板渲染是可行的,只要选择一套同时支持浏览器和 Node.js 的模板引擎就能搞定。咱们写好模板, 在 Node.js 准备好数据,而后将数据灌入模板产出 HTML,输出到浏览器以后由客户端 JavaScript 承载交互,搞定。axios
软件开发中遇到的全部问题,均可以经过增长一层抽象而得以解决后端
思路到了这里,咱们就会发现,『模板』实际上是一种抽象层,虽然底层的 HTML 只能跑在浏览器端,可是顶层的模板却能经过模板引擎同时跑在浏览器和服务器端,此为垂直方向,在水平方向上,模板将数据和结构解耦,将数据灌入结构,这种灌入,实际是一锤子买卖,管生无论养。
随着时间的推动,组件化的大潮来了,其核心概念 Virtual DOM 依其声明式和高性能让前端开发者大呼爽爽爽,但究其本质,就是为了解决频繁操做 DOM 而在 HTML 之上作的一层抽象,与模板不一样的是,它将数据与结构产生交互,有表明的要数 Facebook 方使用的单项数据流和 Vue 方使用的 MVVM 数据流,大道至简,咱们观察函数 UI = F(data), 其中 UI 为最终产出前端界面,data 为数据,F 则为模板结构或者 Virtual DOM,模板的方式是 F 只执行一遍,而组件方式则为每次 data 改变都会再执行一遍。
因此理论上,不管是模板方式仍是组件方式,先后端同构的方案都呼之欲出,咱们在 Node.js 端获取数据 ,执行 F 函数,获得 HTML输出给浏览器,浏览器 JavaScript 复用 HTML,继续执行 F 函数,等到数据变化,继续执行 F 函数,交互也获得解决,完美~~~
但因为组件化大势所趋,下文将略去模板方案,咱们以 Vue 为类比,下图代表其实施思路:
因为 F 同时须要在浏览器端和服务器端执行,因此对于整个 Vue App,咱们须要同时支持两端,也就是通用代码。因此咱们须要将 SPA 架构的代码进行改造:
if (process.env.EXEC_ENV === 'client') { window.addEventListener(...); } if (process.env.EXEC_ENV === 'server') { } 复制代码
至此,白屏问题问题看起来是解决了,经过把 JavaScript 的渲染逻辑放到 Node.js 端进行,咱们加快了首屏出现的时间,可是联想到 Node.js 对前端的赋能,咱们或许能够作的更多。
让咱们把视角移动的更细致一些,关注『从服务器端输出 HTML』这一部分,其隐藏的含义是咱们须要把 App 渲染的全部 HTML 都输出给前端,其实否则,举个栗子:
好比在移动端有一个页面,它有大约 10 屏的高度,若是咱们在服务器端所有输出 10 屏实际上是有点浪费的,咱们能够只输出首屏须要的,从而下降 render 执行时间从而下降 TTFB 时间,让页面更快的到达用户眼前。实践中,通常状况是输出大概快两屏的样子,就能处理因此机型的高度问题,剩下的 8 屏,在浏览器端继续渲染,渐进产出内容,用户无感知。
得益于 Node.js 输出 HTML 的另外一层含义,就是咱们能够直接在首次接触就能感知到客户端,也就有了足够的灵活性,再举个栗子:
有个针对安卓平台和 iOS 平台不一样的脚本只要加载,若是在 SPA 状况下,只有等 JavaScript 执行时咱们断定 navigation.userAgent 来获知先在是哪一个平台,而后在 appendChild 一个 script 到 body,但若是服务端能首次接触就能感知,咱们能够在服务端直接拿到 HTTP 请求中的 userAgent 断定平台,根据标识在模板中处理,很显然,这样很稳。
另外,若是有一些特别复杂的计算,服务端能够有更多的办法将数据更快的处理,以免繁忙无比的浏览器接手。
通常的业务场景下,咱们须要在 Node.js 中经过内网将数据获取到,而后经过 render 函数渲染出 HTML(通常须要将数据附带给 HTML 输出以便重复利用),这个时候咱们能够经过页面访问地址和生成的 HTML 字符串作缓存策略,在缓存(通常选择 redis 等方案)以后,下次直接将一样的页面直接输出到前端,可大幅提升渲染性能。
但这种方案也有不少限制,由于要考虑页面地址、多平台下、帐户是否登陆,页面是否须要改动等状况:
同构渲染看似美好,但其相对传统 SPA 确有着更多挑战:
服务器端渲染相对应传统的 Node.js 应用,renderToString 函数不只 CPU 密集,并且不一样的组件对机器资源的要求不尽相同,这就更须要 Node.js 指标的监控、日志的记录、错误的收集、崩溃机制的完善。这里额外的关键的指标是 renderToString 的时间,它反应了 Node.js 渲染所使用的时间,若是加入缓存机制,就须要统计命中率等等。
关于写通用代码,要求比 SPA 架构对开发者提出了更高的要求,咱们须要当心再当心,由于万一搞错,将致使很难排查的内存泄露和 CPU 飙升,而且一旦出了问题,就像要修理天上跑的飞机同样,很是困难。还记得有一次在相似 componentWillMount 写了一些跟浏览器相关的代码致使的内存飙升,还有一次 JSON.stringify 一个大对象致使的 CPU 飙升,不堪回首。这方面 alinode 作的很好,确实能够知足这种飞机场景。
为了效率, 前端们付出了艰辛的努力,不管是工程上咱们想方设法的制造工具,仍是组件化的引入,咱们解决的是开发的效率,而不管是 Virtual DOM 的引入解决频繁操做的 DOM,仍是用了提高用户体验而使用的 SPA 架构,咱们解决的是用户的使用效率,是前端的性能。而同构渲染也是这样一种方案,它引入了 Node.js 的复杂度,要求咱们写出限制更多的代码,其根本目的仍是为了让用户更快更早的看到页面,那怕是 50 毫秒,那怕是 10 毫秒。
关于咱们
咱们是蚂蚁保险体验技术团队,来自蚂蚁金服保险事业群。咱们是一个年轻的团队(没有历史技术栈包袱),目前平均年龄92年(去除一个最高分8x年-团队leader,去除一个最低分97年-实习小老弟)。咱们支持了阿里集团几乎全部的保险业务。18年咱们产出的相互宝轰动保险界,19年咱们更有多个重量级项目筹备动员中。现伴随着事业群的高速发展,团队也在迅速扩张,欢迎各位前端高手加入咱们~
咱们但愿你是:技术上基础扎实、某领域深刻(Node/互动营销/数据可视化等);学习上善于沉淀、持续学习;性格上乐观开朗、活泼外向。
若有兴趣加入咱们,欢迎发送简历至邮箱:luguang.ylg@antfin.com
本文做者:蚂蚁保险-体验技术组-月影
掘金地址:杨柳岸酱