域名"注册+交易+金融+行情+交流"
域名相关服务一应俱全,旨为您所想。

探索前端性能提升秘诀:揭秘热门优化策略及其显著成效

常见的前端性能提升策略及其效益分析

前端领域庞大复杂,涵盖 HTML、CSS、JavaScript、图片、Flash 等众多资源。前端优化工作繁杂,需针对各类资源采取不同方法。那么,前端优化的目标究竟是什么?

1. 从用户视角看,优化旨在使页面加载速度更快、对用户操作响应更迅速,从而提供更优质的体验。

2. 从服务商视角看,优化有助于降低页面请求数量或减少请求所占带宽,从而节省大量资源。

综上所述,合理的优化不仅能提升网站用户体验,还能有效节约资源。

前端优化方法众多,大致可分为两类:页面级优化和代码级优化。页面级优化包括 HTTP 请求数量控制、脚本无阻塞加载、内联脚本位置优化等;代码级优化则涉及 JavaScript DOM 操作优化、CSS 选择符优化、图片优化、HTML 结构优化等。为提高投入产出比,以下优化策略将按效益大小排序。

一、页面级优化

1. 减少HTTP请求数量

这是所有前端人员都熟知的策略,也是最为关键和有效的。减少请求数量,究竟会产生哪些影响?首先,每个请求都有其成本,包括时间成本和资源成本。一个完整的请求需经历 DNS 解析、建立连接、发送数据、等待响应、接收数据等复杂过程。时间成本即用户需要等待这个过程结束才能看到或“感受”到资源。资源上,每个请求都需要携带数据,因此每个请求都会占用带宽。此外,浏览器并发请求数量有限,过多请求会导致浏览器分批加载,增加用户等待时间,给人留下网站速度慢的印象。

减少HTTP请求数量的主要途径包括:

(1) 从设计实现层面简化页面

若页面结构简单,如百度首页,则无需过多优化。保持页面简洁、减少资源使用是最直接的方法。若页面需要华丽皮肤,请继续阅读以下内容。

(2) 合理设置HTTP缓存

缓存功能强大,恰当的缓存设置可大幅减少HTTP请求。以有啊首页为例,浏览器无缓存时访问需发出78个请求,共600多K数据;而浏览器已缓存后访问仅需10个请求,共20多K数据。若直接刷新页面,效果不明显,因为缓存资源请求服务器会返回304响应,只包含Header,节省带宽。

合理设置缓存的原则是:能缓存越多越好,能缓存越久越好。例如,很少变化的图片资源可通过HTTP Header中的Expires设置较长的过期头;变化不频繁但可能变化的资源可使用Last-Modified进行请求验证。尽可能让资源在缓存中停留更久。关于HTTP缓存的具体设置和原理,此处不再详述,有兴趣的可以参考以下文章:

HTTP1.1协议中关于缓存策略的描述

Fiddler HTTP Performance中关于缓存的介绍

(3) 资源合并与压缩

尽可能将外部脚本、样式合并,多个合并为一个。CSS、JavaScript、图片等资源均可使用相应工具进行压缩,压缩后可节省大量空间。

(4) CSS Sprites

合并CSS图片,减少请求数量的又一有效方法。

(5) Inline Images

使用data: URL scheme将图片嵌入页面或CSS中,若不考虑资源管理问题,不失为一个好方法。若嵌入页面,则会增大页面体积,且无法利用浏览器缓存。使用CSS中的图片则更为理想。

(6) Lazy Load Images(自己对这一块的内容还是不了解)

此策略实际上不一定能减少HTTP请求数量,但在某些条件下或页面刚加载时,可以减少HTTP请求数量。对于图片而言,在页面刚加载时只加载第一屏,当用户继续滚动屏幕时再加载后续图片。这样一来,若用户只对第一屏内容感兴趣,则可节省剩余图片请求。有啊首页曾采用的方法是在加载时将第一屏之后的图片地址缓存到Textarea标签中,待用户滚动屏幕时再“惰性”加载。

  1. 将外部脚本置底(将脚本内容在页面信息内容加载后再加载)

    前文提到,浏览器可以并发请求,这有助于快速加载资源。然而,外链脚本在加载时会阻塞其他资源,如图片、样式等。若将脚本放在页面靠前位置,会影响页面加载速度和用户体验。解决此问题的方法有很多,此处有详细介绍,最简单可靠的方法是将脚本尽量往后挪,减少对并发下载的影响。

  2. 异步执行inline脚本(其实原理和上面一样,保证脚本在页面内容后面加载。)

    inline脚本对性能的影响与外部脚本相似,甚至更严重。首先,与外部脚本一样,inline脚本在执行时会阻塞并发请求。其次,由于浏览器在页面处理方面是单线程的,当inline脚本在页面渲染前执行时,页面渲染工作将被推迟。简而言之,inline脚本执行时,页面处于空白状态。因此,建议将执行时间较长的inline脚本异步执行。异步方式有多种,如使用script元素的defer属性(存在兼容性问题和其他问题,如不能使用document.write)、使用setTimeout等。HTML5中引入的Web Workers机制恰好可以解决此类问题。

内部脚本对性能的冲击与外部脚本相较,毫不逊色。首先,与外部脚本相似,内部脚本在执行过程中同样会阻碍并发请求,除此之外,鉴于浏览器在页面处理上为单线程,当内部脚本在页面渲染前执行时,页面的渲染进程将被延迟。简言之,内部脚本在执行期间,页面处于空白状态。鉴于以上两点,建议将执行时间较长的内部脚本以异步方式执行,异步方法颇多,例如运用 script 元素的 defer 属性(存在兼容性问题及其他问题,如不能使用 document.write)、运用 setTimeout,此外,HTML5 中引入的 Web Workers 机制,恰好能解决此类问题。

  1. 惰性加载 JavaScript(仅在需要加载时加载,一般情况下不加载信息内容。)

    随着 JavaScript 框架的普及,越来越多的网站开始采用框架。然而,一个框架通常包含了许多功能实现,而这些功能并非每个页面都需要,若下载了不必要的脚本,则算得上是一种资源浪费——既浪费了带宽,又浪费了执行时间。目前,主要有两种做法,一种是为流量特别大的页面定制一个专用的 mini 版框架,另一种则是惰性加载。YUI 就采用了第二种方式,在 YUI 的实现中,最初仅加载核心模块,其他模块可等到需要使用时再加载。

  2. 将 CSS 放在 HEAD 中

    若将 CSS 放置于其他位置,如 BODY 中,则浏览器可能还未下载和解析 CSS 就开始渲染页面,这会导致页面从无 CSS 状态跳转到 CSS 状态,用户体验较差。除此之外,某些浏览器会在 CSS 下载完成后才开始渲染页面,若 CSS 放置于靠下位置,则会导致浏览器推迟渲染时间。

  3. 异步请求 Callback(即将一些行为样式提取出来,逐步加载信息内容)

    在某些页面中可能存在这样的需求,需要使用 script 标签来异步请求数据。类似:

    JavaScript:

    function myCallback(info){

    //在此处执行操作

    }

    HTML:

    cb 返回的内容:

    myCallback('Hello world!');

    像以上这种方式直接在页面上写 对页面性能也有影响,即增加了页面首次加载的负担,推迟了 DOMContentLoaded 和 window.onload 事件的触发时机。如果时效性允许,可以考虑在 DOMContentLoaded 事件触发时加载,或使用 setTimeout 方式灵活控制加载时机。

  4. 减少不必要的 HTTP 跳转

    对于以目录形式访问的 HTTP 链接,许多人会忽略链接最后是否带有 '/',若你的服务器对此有所区别对待,那么你也需注意,这之中可能隐藏了 301 跳转,增加了多余请求。具体可参考下图,其中第一个链接是以无 '/' 结尾的方式访问的,于是服务器进行了一次跳转。

  5. 避免重复的资源请求

    这种情况主要是由于疏忽或页面由多个模块拼接而成,然后每个模块中请求了同样的资源时,会导致资源的重复请求。

二、代码级优化

1. JavaScript

(1). DOM

DOM 操作是脚本中最耗性能的一类操作,例如增加、修改、删除 DOM 元素或对 DOM 集合进行操作。若脚本中包含了大量的 DOM 操作,则需注意以下几点:

a. HTML Collection(HTML 收集器,返回的是一个数组内容信息)

在脚本中,document.images、document.forms、getElementsByTagName() 返回的都是 HTMLCollection 类型的集合,在平时使用时大多将其作为数组来使用,因为它有 length 属性,也可以使用索引访问每个元素。然而,在访问性能上则比数组要差很多,原因是这个集合并非一个静态的结果,它表示的仅仅是一个特定的查询,每次访问该集合时都会重新执行这个查询从而更新查询结果。所谓的“访问集合”包括读取集合的 length 属性、访问集合中的元素。

因此,当你需要遍历 HTML Collection 时,尽量将其转换为数组后再访问,以提高性能。即使不转换为数组,也请尽可能少地访问它,例如在遍历时可以将 length 属性、成员保存到局部变量后再使用局部变量。

b. Reflow & Repaint

除了上述一点之外,DOM 操作还需考虑浏览器的 Reflow 和 Repaint,因为这些都是需要消耗资源的,具体可参考以下文章:

如何减少浏览器的 repaint 和 reflow?

Understanding Internet Explorer Rendering Behaviour

Notes on HTML Reflow

(2). 谨慎使用 with

with(obj){ p= 1}; 代码块的行为实际上是修改了代码块中的执行环境,将 obj 放在了其作用域链的最前端,在 with 代码块中访问非局部变量都是从 obj 上开始查找,如果没有再依次按作用域链向上查找,因此使用 with 相当于增加了作用域链长度。而每次查找作用域链都是要消耗时间的,过长的作用域链会导致查找性能下降。

因此,除非你能肯定在 with 代码中只访问 obj 中的属性,否则谨慎使用 with,替代的可以使用局部变量缓存需要访问的属性。

(3). 避免使用 eval 和 Function

每次 eval 或 Function 构造函数作用于字符串表示的源代码时,脚本引擎都需要将源代码转换成可执行代码。这是一项非常消耗资源的操作——通常比简单的函数调用慢 100 倍以上。

eval 函数效率特别低,由于事先无法知晓传给 eval 的字符串中的内容,eval 在其上下文中解释要处理的代码,也就是说编译器无法优化上下文,因此只能由浏览器在运行时解释代码。这对性能影响很大。

Function 构造函数比 eval 略好,因为使用此代码不会影响周围代码;但其速度仍然很慢。

构造函数比评估函数更优,因为此代码的使用不会影响周围代码;但其速度仍然较慢。

此外,使用评估函数和构造函数也不利于JavaScript压缩工具执行压缩操作。

(4).减少作用域链搜索(涉及一些相关内容的问题)

上文提到了作用域链搜索问题,这一点在循环中尤其需要注意。如果在循环中需要访问非本作用域下的变量时,请在遍历之前用局部变量缓存该变量,并在遍历结束后再重写那个变量,这一点对全局变量尤其重要,因为全局变量处于作用域链的最顶端,访问时的查找次数是最多的。

低效的写法:

//全局变量

var globalVar= 1;

function myCallback(info){

for( var i= 100000; i--;){

//每次访问 globalVar都需要查找到作用域链最顶端,本例中需要访问 100000次

globalVar+= i;

}

}

更高效的写法:

//全局变量

var globalVar= 1;

function myCallback(info){

//局部变量缓存全局变量

var localVar= globalVar;

for( var i= 100000; i--;){

//访问局部变量是最快的

localVar+= i;

}

//本例中只需要访问 2次全局变量

在函数中只需要将 globalVar中内容的值赋给localVar中区

globalVar= localVar;

}

此外,要减少作用域链搜索还应该减少闭包的使用。

(5).数据访问

JavaScript中的数据访问包括直接量(字符串、正则表达式)、变量、对象属性以及数组,其中对直接量和局部变量的访问是最快的,对对象属性以及数组的访问需要更大的开销。当出现以下情况时,建议将数据放入局部变量:

a.对任何对象属性的访问超过 1次

b.对任何数组成员的访问次数超过 1次

另外,还应当尽可能减少对对象以及数组深度搜索。

(6).字符串连接

在JavaScript中使用"+"号来连接字符串效率是比较低的,因为每次运行都会开辟新的内存并生成新的字符串变量,然后将连接结果赋值给新变量。与之相比更为高效的做法是使用数组的join方法,即将需要连接的字符串放在数组中最后调用其join方法得到结果。不过由于使用数组也有一定的开销,因此当需要连接的字符串较多的时候可以考虑用此方法。

关于JavaScript优化的更详细介绍请参考:

Write Efficient JavaScript(PPT)

Efficient JavaScript

2. CSS选择器

在大多数人的观念中,都觉得浏览器对CSS选择器的解析是从左往右进行的,例如

toc A{ color:#444;}

这样一个选择器,如果是从右往左解析则效率会很高,因为第一个ID选择基本上就把查找的范围限定了,但实际上浏览器对选择器的解析是从右往左进行的。如上面的选择器,浏览器必须遍历查找每一个A标签的祖先节点,效率并不像之前想象的那样高。根据浏览器的这一行为特点,在编写选择器的时候需要注意很多事项,有人已经一一列举了,详情参考此处。

3. HTML

对HTML本身的优化现如今也越来越多地受到关注,详情可以参见这篇总结性文章。

4. 图片压缩

图片压缩是个技术活,不过现如今这方面的工具也非常多,压缩之后往往能带来不错的效果,具体的压缩原理以及方法在《Even Faster Web Sites》第10章有很详细的介绍,有兴趣的可以去看看。

总结

本文从页面级以及代码级两个层面总结了对前端优化的各种方法,这些方法基本上都是前端开发人员在开发过程中可以借鉴和实践的。除此之外,完整的前端优化还应该包括很多其他的途径,例如CDN、Gzip、多域名、无Cookie服务器等等,由于对于开发人员的可操作性并不强,在此也就不多叙述了,详细的可以参考Yahoo和Google的这些“金科玉律”。

常用的前端性能优化方法有哪些

性能优化,就是在不影响系统运行正确性的前提下,使之运行得更快,完成特定功能所需的时间更短。为了实现这一效果,我们应当尽量提前进行性能优化,未雨绸缪,甚至最好是将它作为一个周期性的工作来进行。

一个好的性能优化思路应该分为四步:

明确优化目的。优化的目的可以是提升用户体验,比如消除一些有明显卡顿的页面和操作,还可以是节省服务器带宽流量、减轻服务器压力等。无论如何,你需要有一个目的。有很多人只是为了优化而优化,目的丢了,或者甚至一开始就没考虑过,只是不断追求更好看的性能指标。

确定要做到什么程度。

优化是永无止境的,为了避免陷入前面说的无意义的性能黑洞中,我们最好能够根据实际的业务情况定义出一个相对客观的标准,代表优化到什么程度。比如,根据当下的性能指标与业务量对比,发现最大并发数可能会超过当前的2倍,那么此时优化到争取优化提升3倍,至少保证能提升2.5倍,是一个比较合理的标准。

请点击输入图片描述

请点击输入图片描述

3、找到瓶颈点

大部分情况下,流程上的优化远胜于语法级别的优化,所以我们最好还是能够借助一些客观数据,以获得更多的运行环境相关的信息,来找到整个“木桶”上最短的一块“板”。如整个系统的总体架构、服务器的信息等,便于定位到底性能的瓶颈点在哪。

4、着手优化

做优化的正确思路一般符合下面两个方向。

第一,空间换性能。一个节点顶不住就多复制一个节点出来,独一份的数据导致资源竞争得厉害,就多复制一份数据出来。

第二,距离换性能。数据从服务端经过层层处理返回到客户端觉得慢的话,那么能不能直接保存在客户端,或者至少是离客户端尽可能近的地方。

第二,谈及数据传输的时效性。当发现数据从服务端经过多级处理反馈至客户端显得迟缓时,是否可以考虑将其直接存储在客户端,或者至少将其保存在距离客户端较近的位置。

性能提升是Web前端工程师必须掌握的基本技能之一。若想获得较高的薪酬,就必须拥有坚实的理论基础和丰富的实战经验,而这都需要通过系统的学习和大量的项目实践来积累。

以上所转载内容均来自于网络,不为其真实性负责,只为传播网络信息为目的,非商业用途,如有异议请及时联系btr2020@163.com,本人将予以删除。夫唯域名网 » 探索前端性能提升秘诀:揭秘热门优化策略及其显著成效

分享到: 生成海报