前端性能优化的常用技巧有哪些
性能提升,指在保证系统运行正确性的基础上,加快其运行速度,缩短完成特定功能所需的时间。为了达成这一目标,我们应尽量提前着手性能提升,未雨绸缪,甚至最好将其纳入周期性工作之中。
一项优秀的性能提升策略应分为四个步骤:
确立提升目标。提升的目标可以是提升用户体验,例如消除卡顿明显的页面和操作,还可以是节约服务器带宽流量、减轻服务器负担等。无论如何,你必须有一个明确的目标。许多人只是为了提升而提升,失去了目标,甚至一开始就没有考虑过,只是不断追求更好的性能指标。
明确提升程度。
提升是一个无止境的过程,为了避免陷入前面提到的无意义的性能黑洞,我们最好能够根据实际的业务情况制定出一个相对客观的标准,代表提升到何种程度。比如,根据当下的性能指标与业务量对比,发现最大并发数可能会超过当前的2倍,那么此时提升到争取提升3倍,至少保证能提升2.5倍,是一个相对合理的标准。
3、寻找瓶颈点
大部分情况下,流程上的提升远胜于语法级别的提升,所以我们最好还是能够借助一些客观数据,以获取更多的运行环境相关信息,来找到整个“木桶”上最短的一块“板”。如整个系统的总体架构、服务器的信息等,便于定位到底性能的瓶颈点在哪。
4、开始提升
进行提升的正确思路一般符合以下两个方向。
第一,以空间换速度。一个节点承受不住就多**一个节点出来,独一份的数据导致资源竞争激烈,就多**一份数据出来。
第二,以距离换速度。数据从服务端经过层层处理返回到客户端觉得慢的话,那么能不能直接保存在客户端,或者至少是离客户端尽可能近的地方。
性能提升只是Web前端人员需要掌握的基本技能之一,想要获得高薪,你需要具备扎实的理论基础以及丰富的实战经验,而这些需要系统的学习以及较多的项目积累。
常见的前端性能提升手段及其收益
前端领域十分庞大,包括HTML、CSS、JavaScript、Image、Flash等各种资源。前端提升是复杂的,针对各种资源都有不同的方法。那么,前端提升的目的是什么?
1.从用户角度而言,提升可以让页面加载更快、对用户的操作响应更及时,能够提供更为友好的体验。
2.从服务商角度而言,提升可以减少页面请求数、或者减小请求所占带宽,能够节省可观的资源。
总之,恰当的提升不仅能够改善站点的用户体验并且能够节省相当的资源利用。
前端提升的途径有很多,按粒度大致可以分为两类,第一类是页面级别的提升,例如HTTP请求数、脚本的无阻塞加载、内联脚本的位置优化等;第二类则是代码级别的提升,例如JavaScript中的DOM操作优化、CSS选择符优化、图片优化以及HTML结构优化等等。另外,本着提高投入产出比的目的,后文提到的各种提升策略大致按照投入产出比从大到小的顺序排列。
一、页面级提升
1.减少HTTP请求数
这条策略基本上所有前端人都知道,而且也是最重要最有效的。都说要减少HTTP请求,那请求多了到底会怎么样呢?首先,每个请求都是有成本的,既包含时间成本也包含资源成本。一个完整的请求都需要经过DNS寻址、与服务器建立连接、发送数据、等待服务器响应、接收数据这样一个“漫长”而复杂的过程。时间成本就是用户需要看到或者“感受”到这个资源是必须要等待这个过程结束的,资源上由于每个请求都需要携带数据,因此每个请求都需要占用带宽。另外,由于浏览器进行并发请求的请求数是有上限的(具体参见此处),因此请求数多了以后,浏览器需要分批进行请求,因此会增加用户的等待时间,会给用户造成站点速度慢这样一个印象,即使可能用户能看到的第一屏的资源都已经请求完了,但是浏览器的进度条会一直存在。
减少HTTP请求数的主要途径包括:
(1)从设计实现层面简化页面
如果你的页面像百度首页一样简单,那么接下来的规则基本上都用不着了。保持页面简洁、减少资源的使用是最直接的。如果不是这样,你的页面需要华丽的皮肤,则继续阅读下面的内容。
(2)合理设置HTTP缓存
缓存的力量是强大的,恰当的缓存设置可以大大减少HTTP请求。以有啊首页为例,当浏览器没有缓存的时候访问一共会发出78个请求,共600多K数据(如图1.1),而当第二次访问即浏览器已缓存之后访问则仅有10个请求,共20多K数据(如图1.2)。(这里需要说明的是,如果直接F5刷新页面的话效果是不一样的,这种情况下请求数还是一样,不过被缓存资源的请求服务器是304响应,只有Header没有Body,可以节省带宽)
怎样才算合理设置?原则很简单,能缓存越多越好,能缓存越久越好。例如,很少变化的图片资源可以直接通过HTTP Header中的Expires设置一个很长的过期头;变化不频繁而又可能会变的资源可以使用Last-Modifed来做请求验证。尽可能让资源能够在缓存中待得更久。关于HTTP缓存的具体设置和原理此处就不再详述了,有兴趣的可以参考下列文章:
HTTP1.1协议中关于缓存策略的描述
Fiddler HTTP Performance中关于缓存的介绍
(3)资源合并与压缩
如果可以的话,尽可能将外部的脚本、样式进行合并,多个合为一个。另外,CSS、JavaScript、Image都可以用相应的工具进行压缩,压缩后往往能省下不少空间。
若能实现,尽量将外部脚本、样式整合,多者合一。此外,CSS、JavaScript、图片均可用相应工具进行压缩,压缩后通常能节省大量空间。
(4). CSS Sprites
将CSS图片合并,减少请求次数的又一良策。
(5). Inline Images
采用data: URL方案将图片嵌入页面或CSS中,若不考虑资源管理问题,亦不失为一策。若嵌入页面,则会导致页面体积增大,且无法利用浏览器缓存。而在CSS中使用图片则更为理想。
(6). Lazy Load Images(我对这一领域尚不熟悉)
此策略未必能减少HTTP请求数,但在某些条件下或页面初始加载时,可减少HTTP请求数。对于图片而言,初始加载时仅加载第一屏,当用户滚动时再加载后续图片。如此一来,若用户仅关注第一屏内容,则可节省剩余图片请求。
2. 将外部脚本置于底部(将脚本内容在页面信息内容加载后再加载)
前文已述,浏览器可并发请求,这一特性使其能更快地加载资源。然而,外链脚本在加载时会阻塞其他资源,如脚本加载完成前,其后的图片、样式及其他脚本都处于阻塞状态,直至脚本加载完成。若将脚本置于页面前端,会影响页面加载速度,进而影响用户体验。解决此问题的方法有很多,此处有详细介绍(此处为译文及更详细示例),最简单可行的方法是将脚本尽可能后移,减少对并发下载的影响。
3. 异步执行inline脚本(原理与上述类似,确保脚本在页面内容后面加载。)
inline脚本对性能的影响不亚于外部脚本。首先,与外部脚本一样,inline脚本在执行时会阻塞并发请求。其次,由于浏览器在页面处理方面是单线程的,当inline脚本在页面渲染前执行时,页面渲染工作将被推迟。简言之,inline脚本执行时,页面处于空白状态。鉴于以上两点,建议将执行时间较长的inline脚本异步执行,异步方式有多种,如使用script元素的defer属性(存在兼容性问题及其他问题,如不能使用document.write)、使用setTimeout,此外,HTML5中引入了Web Workers机制,可解决此类问题。
4. Lazy Load JavaScript(仅在需要加载时加载,一般情况下不加载信息内容。)
随着JavaScript框架的流行,越来越多的站点开始使用框架。但一个框架通常包含许多功能实现,并非每个页面都需要这些功能。若下载了不需要的脚本,则是一种资源浪费——既浪费了带宽,又浪费了执行时间。目前,有两种做法:一种是为流量特别大的页面定制一个专用的mini版框架,另一种则是Lazy Load。YUI便采用了第二种方式,最初仅加载核心模块,其他模块可待需要时再加载。
5. 将CSS置于HEAD中
若将CSS置于BODY中,则浏览器可能还未下载和解析CSS就开始渲染页面,导致页面由无CSS状态跳转到CSS状态,用户体验较差。此外,有些浏览器会在CSS下载完成后才开始渲染页面,若CSS置于页面下方,则会导致浏览器推迟渲染时间。
6. 异步请求Callback(即将一些行为样式提取出来,逐步加载信息内容)
在某些页面中,可能需要使用script标签异步请求数据。类似:
JavaScript:
function myCallback(info){
//在此处执行操作
}
HTML:
cb返回的内容:
myCallback('Hello world!');
如上直接在页面上写