0.1秒的价值!浅谈Web前端页面提速问题
记得面试现在这份工作的时候,一位领导语重心长地谈道——当今的世界是互联网的世界,IT企业之间的竞争是很激烈的,如果一个网页的加载和显示速度,相比别人的站点页面有那么0.1秒的提升,那也是很大的一个成就。
然后我不知道怎么写下去了,就在群里问了那群狗头军师,结果是这样的。。。
好的,是时候“语锋一转”切回主题了 —— 如何提升我们站点页面的访问速度、减少等待时间,从而最大化地提升用户访问体验呢?
针对这个问题,我们今天会从前端的角度来提出系列解决方案,它们都能有效地提升你页面的访问速度。
一. 减少对服务器的文件请求
常规的HTTP请求属于“请求”-“应答”-“断开”形式的短连接,每一个独立的资源我们都会向服务器发去一份get请求,再等服务端将我们需要的文件传回来。每一次资源的请求都实实在在地耗费了一次“连接-等待-接收”的时间(当然将http请求设为keep-alive长连接状态可以减少“连接”的次数和时间),如果我们能有效减少对服务器文件的请求次数,便意味着我们可以从这块省下一些页面等待时间,也可以顺便减少服务器的负担。
对于这个解决方案,我们可以这么做:
1. 使用css sprite技术合并多个图片为单个图片文件,实际使用时通过background-position来定位背景位置(相信大家第一个想到的也是这个吧);
2. 合并多个css样式文件为单个样式文件,合并多个脚本为单个脚本,再在页面中引用合并后的样式/脚本文件。对于这个你可以使用r.js来帮忙,但我个人倒是不怎么推荐这个方法,因为合并了文件之后,多个页面之间公共部分的样式/脚本文件就无法缓存到客户端了;
3. 使用base64编码来展示图片。就如图github 404页面那样:
你可以使用这个工具来帮你把图片转换为base64编码的文件流,但常规只推荐你把这种方式使用在用户重复访问量较少的页面,因为它们虽然无须从服务端get一遍,但也无法缓存在客户端,导致用户每次访问页面都要重新渲染一次。而且冗长的文件流代码会占用你页面很大的代码空间,维护起页面来估计也会挺心塞;
4. 将小块的css、js代码段直接写在页面上,而非在页面引入独立的样式/脚本文件。相信有的朋友看惯了“保持结构 (标记)、表现 (样式)、行为 (脚本)三者分离”的规范,对此观点可能有些意见。只能说规范不是教条,适合自己的才是硬道理。直接把小段的、复用率低的样式/脚本直接写于页面上带来的利还是大于弊的(弊可能也就是增大了页面代码量、不那么好维护了点)。反观所有主流门户网站的页面源文件,基本没有一个是把样式/脚本都全部作为外部文件引入的(无论他们是否从减少服务器请求这点出发,事实都是这样);
5. 利用http-equiv="expires"元标签,设定一个未来的某时间点作为页面文件过期时间,用户在过期时间之前所获取到的页面文件都仅从缓存中去取。不过这个办法太死板(有时候即使服务端及时把过期时间更改为已结束时间,客户端可能都不会按照新更改的规则去服务端获取新文件资源),常规是不推荐使用的。
二. 减少文件大小
文件太大(特别是图片)导致加载时间较长,往往都是影响页面加载体验的头号大敌,那么尽可能减少请求文件的大小便是相当重要的事情了,我们可以做的事情有:
1. 压缩样式/脚本文件,就此你可以使用gulp或者grunt来实现这点,它们均能很好地减少css/js文件的大小(对于js还能起到混淆变量、函数名的作用);
2. 针对性选择图片格式,在无透明背景需求下,对于颜色较单一、无色彩渐变的图片仅使用gif格式,对于jpg图片也可按照其清晰度要求,在导出jpg的时候选择对应的“品质”进行优化:
如果你喜欢尝鲜,可以学淘宝那样使用webp图片格式,它能很好地优化同画质下的文件大小:
3. 使用Font Awesome来替代页面上的图标,其原理是使用@font-face让用户下载一个非常小的UI字体包,把页面上用到的图标以字符的形式来显示,从而减少了图片需求和图标文件大小。
三. 适度使用CDN
使用CDN有几个好处:如果用户在其它站点下载过这个CDN资源,那么来我们站点仅仅从缓存获取即可;减少了对自己站点服务器的文件请求(外部CDN的情况下),减少服务器负担;多个域会使浏览器允许异步下载资源的最大数量增多,比如一个站点只从一个域来请求资源,那么FireFox只允许同时刻最多异步下载2个文件,但如果使用了外部CDN来引入资源,那么FF允许在同时异步下载本域中的两个资源外,还额外允许同时异步下载另一个域(CDN)下的2个资源。
但是使用CDN有一个很大的问题——增加了dns解析的开销,如果一个页面同时引入了多个CDN的资源,可能会因为dns解析而陷入较多的等待时间,导致得不偿失。
对于这个问题,常规是建议一个站点下只使用同一个可靠、快速的CDN来引入各种所需资源即可,也就是说,建议一个页面从2个不同的域(比如站点域和CDN域)下来请求资源是最佳的选择(据说这个结论是雅虎前端工程师提出的)。
四. 延迟请求、异步加载脚本
在各主流浏览器下,常规情况,我们的脚本文件跟随其它资源文件一样都是异步下载的,但这里存在一个问题——比如FireFox下载好脚本后的一小段时间内会有“执行阻塞”的情况发生,也就是说浏览器下载好脚本后执行它的这段时间里,浏览器的其它行为被阻塞,导致页面上的其它资源都是无法被请求和下载的:
如果你页面里存在js代码执行时间过长的情况,那么用户就会明显感觉到页面的延迟。解决这个问题有一个简单的方法——将脚本请求标签放置到