简单来说,今天要说的是性能问题。大家上网时都想马上看到自己想看的内容,而且我们也发现快速的启动能提高用户满意度。因此,当我们着手做期待已久的Neflix.com升级任务时,网站的界面工程团队把启动性能作为最高优先级的任务。
这次我们把启动时间缩短了70%,主要在以下三个方面做了改善:
1. 服务器和客户端渲染
2. 通用JavaScript
3. 降低JavaScript负载
服务器和客户端渲染
旧版netflix.com网站堆栈在服务器标记语言和客户端转化之间有着硬性的分离。这主要是由于我们应用的各个部分使用了不同的编程语言。在服务器端,我们使用的是配合Tomcat,Struts和Tiles使用的Java语言。在浏览器端,我们通过JavaScript来转化服务器生成的标记语言,主要使用jQuery。
这样的分离导致启动时间很不理想。每当用户访问netflix.com上的页面,我们的Java层响应的时候都要处理整个页面并转成HTML发送。于是用户需要一直等待直到整个页面完全生成,而其中大部分内容他们并不感兴趣。
我们的新架构只渲染页面的一小部分,自动生成客户端视图。我们可以改变服务器生成的总视图的数量,这样便于观察因此产生的正面或负面的影响。服务器响应时需要的数据很少,将数据转换为DOM元素时花的时间也更少。客户端JavaScript接管后,它会检查当前视图的数据以及后续会话要求的视图的数据。这样做最大好处是减少了服务器处理时间,并且把渲染整合到同一种语言中。
我们发现服务器和客户端分别渲染还提供了一种灵活性,允许我们选择哪些部分在服务器端渲染,哪些在客户端渲染,于是实现了更快的启动和视图之间的平滑切换。
通用的JavaScript
为了在服务器端和客户端支持相同的渲染,我们要重新考虑我们的渲染路径。必须放弃我们以前的架构中把在服务器端生成标记语言与在客户端进行强化这二者分离的做法。
三大痛点促使我们使用新的Node.js架构:
1. 语言之间的内容切换不理想。
2. 强化标记语言需要太多用于生成标记语言的服务器专用代码和用于强化的客户端专用代码的直接耦合。
3. 我们更倾向于使用同一种API生成所有的标记语言。
针对这个问题,实际上有许多不需要用到Universal JavaScript的解决方法,但是基于如下原因我们发现这个方案是最合适的:如果同一个东西有两个副本,那这两个副本之间很容易产生微小的差异。使用Universal JavaScript意味着只需要把渲染逻辑发送到客户端。
Node.js和React.js天然适用于这类应用场景。通过Node.js和React.js,我们可以在服务器进行渲染,然后在最初的标记语言和React.js的内容发送到浏览器之后,再在客户端对整体的变化进行渲染。这种灵活性允许应用程序在不同的位置独立地进行相同的渲染输出。服务器端和客户端的硬性分离不复存在,它们的渲染输出也就不会有所不同。
降低JavaScript负载的影响
在网站上实现丰富的交互体验,对用户来说往往会转变为沉重的JavaScript负载。在我们的新架构中,我们特别注意把一些大的依赖库替换成多个小模块,并且只对当前访问的用户发送适合他们的JavaScript。
很多在旧架构中用过的大的依赖库没有继续用在新架构中,我们把它们替换成一批新的、更有效率的库。更新这些库使得JavaScript的负载变小了很多,这意味着人们在访问的时候只需要很少的JavaScript。我们知道这个部分仍然有很多要完善的地方,我们正在努力使得JavaScript负载进一步下降。
交互时间
为了了解这些改进带来的影响,我们监控了一个叫交互时间(tti)的参数。
交互时间指的是应用平台首次启动与UI界面互动响应之间的时间量。注意,这并不需要UI界面完全加载,而是用户可以使用输入设备与UI进行交互的第一时间点。
对于在浏览器中运行的应用,这个数据可以从Navigation Timing API得到。
工作仍在继续
我们坚信,高性能并不是一个随意的工程目标——它是创造良好用户体验的硬性要求。我们已经在启动性能方面取得了显著进步,并将在致力于为用户持续提供更好的用户体验的过程中,不断尝试挑战行业的最佳做法。
接下来的几个月,我们会研究Service Workers, ASM.js, Web Assembly和其它新兴的网络标准,看看能否利用它们实现更高性能的网站体验。