关于前端性能优化
EdgeLight opened this issue · 3 comments
关于这个问题,实际上是因为在参与开发毕业季小游戏的时候,遇到了一些性能方面的问题。虽然小游戏不是传统意义上的web界面,仍然有许多问题可以推广到一般的Web前端性能优化问题上。由于这个问题涉及太广,我就根据自己所见和一些资料简单讨论一下,主要讲的是关于加载的优化,然而仅仅只是涉及前端优化这片汪洋大海中的一粒水滴而已。
1.为什么要优化
首先是近来Web前端的技术被炒得越来越火,人们不再能够接受2000年时候那样简陋的网页设计,各类UI交互和动画,以及为了实时改变页面频繁操作的Ajax请求都使得网页的操作量比之前高了许多;同时,主战场逐渐向移动端转型的Web页面,也不得不重新面对不同浏览器、机型、系统的兼容和支持问题。
大部分情况下,移动端的硬件性能本身就弱于PC端,并且不少情况下移动端可能是通过3G/4G信号连接的,因此经常会出现明明在PC端非常流畅的页面,在移动端测试时会卡的一比的情况(不排除某浏览器辣鸡的可能)。在暂时没有办法通过代码优化进一步提升性能的情况下,最容易考虑到的就是为移动端专门布置一个页面,这样保留了PC端的强大交互,也能让移动端有一个能够接受的基本的内容和视觉效果。
这里所说的即『渐进增强(progressive enhancement)』和『优雅降级(graceful degradation)』的观点:
渐进增强 progressive enhancement:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。渐进增强”观点则认为应关注于内容本身。
内容是我们建立网站的诱因。有的网站展示它,有的则收集它,有的寻求,有的操作,还有的网站甚至会包含以上的种种,但相同点是它们全都涉及到内容。这使得“渐进增强”成为一种更为合理的设计范例。这也是它立即被 Yahoo! 所采纳并用以构建其“分级式浏览器支持 (Graded Browser Support)”策略的原因所在。
优雅降级 graceful degradation:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。“优雅降级”观点认为应该针对那些最高级、最完善的浏览器来设计网站。而将那些被认为“过时”或有功能缺失的浏览器下的测试工作安排在开发周期的最后阶段,并把测试对象限定为主流浏览器(如 IE、Mozilla 等)的前一个版本。在这种设计范例下,旧版的浏览器被认为仅能提供“简陋却无妨 (poor, but passable)” 的浏览体验。你可以做一些小的调整来适应某个特定的浏览器。但由于它们并非我们所关注的焦点,因此除了修复较大的错误之外,其它的差异将被直接忽略。
区别:优雅降级是从复杂的现状开始,并试图减少用户体验的供给,而渐进增强则是从一个非常基础的,能够起作用的版本开始,并不断扩充,以适应未来环境的需要。降级(功能衰减)意味着往回看;而渐进增强则意味着朝前看,同时保证其根基处于安全地带。
举个栗子,慕课网(http://www.imooc.com/) 对于PC端和移动端给出了几乎完全不同的的页面,不仅仅是布局上,移动端在首页提供的内容也大幅减少,而且在PC上的动画效果和照片墙也全部移除,只保留最基本的登录和进入内容的交互。
至于需要参考哪一种逻辑,就要根据网站的核心是在什么地方而定的了,慕课网的在线测试等等均在PC端上实现,并且考虑到代码工作是在PC上完成,因此其采用了优雅降级的**,只为移动端保留最最基本的页面,这样自欺欺人地做了针对移动端的优化(并没有)。
2.HTTP与资源加载
加载一直是个令人头疼的问题。有数据表明,如果用户打开一个网站,等待3-4 秒还没有任何反应,他们会变得急躁,焦虑,抱怨,甚至关闭网页并且不再访问,这是非常糟糕的情况。这次在完成毕业季小游戏的时候,相继出现了梯仔闪烁、加载页面文字不出来,以及部分机型的样式还没实现渲染出来就显示了的问题(@林键,手动滑稽)。由于各个资源的加载顺序并不是能够完美控制的,有时会出现问题,比如:
以上是添加了毕业季小游戏加载界面(已经没什么人玩了的时候才添加的没用的页面)后的加载顺序,因为加载页面时就要使用外部字体,因此优先级很高,然而不管怎么提程,仍然会出现文字加载不出来的情况,后来看了一下Timeline:
由于字体文件相对太大(1.9M),即使优先加载也会耗费很长的时间,这段时间是无法显示带字体的文字的。本来打算精简字库(通过将要使用的文字全部提取出来),但是有字库审核还要一段时间,所以简单处理了一下,把页面加载需要用到的几个文字做成png放了上去。不过真正来说,如果时间允许,做成精简字库应该会大大减少字体文件的体积,从而优化加载速度,缺点则是每次改动文案都需要重新生成一个字库文件。
这里可以看出其中一个优化加载速度的方法就是尽量减少每一个资源的体积。这里通常有如下的方法:
- 对于图片,使用合适的图片格式,能够在达到完美的显示效果时使用更小的体积。(简单提及一下)
- 对于比较大的文本文件(例如题库、特别大的css样式表什么的),有必要开启gzip压缩,因为gzip对于有重复“单词”的文本文件,压缩率非常高(例如css文件),可达到85%以上。
- 对于一些库文件,尤其是控件插件型的库(如BootStrap、Flat UI什么的),类似字体文件的精简办法,抽取需要用到的控件代码,而不是整个调用。
除了减少每一个资源的体积以外,另一个很有必要的优化则是尽量减少同一域下的HTTP请求数。在每个资源并不是非常大的情况下,减少HTTP请求数能显著提高加载速度。这里分两个部分来说。
第一个部分是同一域。浏览器常常限制了对相同域名发起的并发连接数的上限。举个栗子,IE6/7和Firefox2(无视我举了这么远古的例子,然而目前还是有很多人用这些浏览器的,哭)设定了同时只能对一个域名发起两个并发连接,新版本的一些浏览器则普遍设置为4-8个。这里澄清一下的是,浏览器作为一个善意的客户端,限制连接数上限是为了保证服务器不会压力过大,否则就变成了DDoS攻击的说。如果需要对某个域建立更多的连接,则需要在当前传输结束后,重复使用或者重新建立TCP连接。
在确保服务器不会压力过大时,为了提高资源传输速度,可以把静态资源放在非主域名下。这样做的好处不仅仅是可以增加浏览器资源请求的并发,还可以减少HTTP请求中所携带的不必要的cookie数据(即使是子域名也会被认为是不同的域名,从而不携带主域名的cookie)。
第二个部分是请求数。之前在看昊杰他们开发治愈系的时候曾经有一个小BUG,按钮定位有偏差导致出现了两个不完整的按钮显示在一个位置上,之前不能理解为什么会出现这样的图片,现在知道这是雪碧图(Sprite Image),即把许多小图拼接在一起,通过CSS定位来显示雪碧图的特定部分,从而有效减少了需要请求的资源数。
其他减少请求数的方法比如删去不必要的HTTP请求,例如将小型css内嵌处理、设置缓存等等,最终目的都是优化HTTP请求数,这样在优化前端加载时间的同时,也能大大减轻对服务器的压力。
下面提一个估计我们暂时用不到的东西。之前雪儿讲了关于HTTP的内容,这个部分在页面首次加载的时候流程是:客户端建立连接,服务器同意连接,客户端发起请求,服务器返回数据,客户端接受并处理数据,这里常常有两个问题:
- HTTP协议的底层协议TCP/IP协议规定三次握手才建立一次连接,而每一个新增的请求都要重新建立TCP/IP连接,重新消耗资源并浪费时间,并且无论是Apache、Nginx甚至是Node.js,尽管消耗资源不同,同样没有这个解决问题。
- 现有的加载模型是阻塞的,也就是说,服务器计算生成页面完成后,才开始网络传输,等整个页面全部传输到客户端(浏览器),在浏览器中渲染仍然需要一定时间,效率非常低。下图为常见的阻塞式HTTP加载。
Facebook公司作为前端的弄潮儿之一,为了应对他们大量数据的情况,开发了BigPipe这一非阻塞的模型,能够解决上面两个问题。通俗来讲,BigPipe首先将HTML分为很多部分,然后在服务器和浏览器之间建立一条管道,通过这条管道,一个框架性的HTML结构首先被传输,其定义了不同的Pagelet模块的位置和宽高(实际上是空的)。服务器传输完成后,紧接着告诉浏览器:这次请求还未结束,保持连接不断开,但浏览器可以先渲染这个“混凝土框架”,空内容显示为“正在加载“。页面渲染的同时,服务器仍然通过这条“管道”源源不断地把资源传输过来,根据重要程度优先传输和渲染(比如主要的用户框架优先显示),并且边渲染边传输,直到将优先度较低的内容传输完成。以下为BigPipe的非阻塞模型。
顺便一提,BigPipe是通过HTTP1.1中的分块传输编码实现的。其允许服务器为动态生成的内容维持HTTP长连接,只要设置HTTP消息的Transfer-Encoding消息头值为chunked,即告知在连接结束前,可以发送任意块,以大小为0的块作为结束标志。不过目前我们的产品需要动态生成的内容并不是那么多,因此这个貌似没并有什么卵用(真要做的话重构太麻烦了坑就给下一届吧)
3.缓存
嗯之前耀宗已经详细的讲了关于缓存的部分,因此这里简单提提就好。(总算可以偷懒了)
缓存对优化起到举足轻重的作用,有些时候优化算法、压缩图片,效果都比不上优化缓存。关于优化缓存的部分暂不赞述,这里提一个问题:文件有可能在运营商服务器上被劫持。比如说我们缓存的一个main.js?version=1,当下次更新后更改Query String,改为main.js?version=2,按照HTTP规范理应重新请求,但是运营商仍然可能拿自己节点服务器上缓存的main.js?version=1来代替。所以为了保证更新,最好还是使用更改文件名的方式,而不是修改Query String。
Web优化的部分实在太多,水平、时间有限难以一一提及,这里推荐一本书《高性能网站建设指南》 ,非常详细地介绍了Web优化的内容,前后端的同学都可以看看哟。
(完)
by 张海鑫
你这一下把坑挖的好深。。。
把库里面有用的部分抽出来。。。
顺便问个问题:惰性加载可以起到性能优化的作用吗?
关于库抽出来什么的,毕竟是有工具网站可以用的(之前不小心翻到的,昊杰也提到过,虽然我现在一下子也找不到了……)
像B站之前有段时间用的是瀑布流,感觉挺炫的,某种意义上加载也很快,目前的B站客户端貌似还是这种的吧
说抽就抽,你们考过库的感受吗
(库:请给我最起码的尊严Orz