前端性能优化篇

前端性能优化篇

首先附上连接:yahoo的优化原则=》https://developer.yahoo.com/performance/rules.html?guccounter=1

本文摘自《摘自高性能网站建设指南》

前端性能优化策略图(图片源自网络)

这里写图片描述

时间都去了哪里?

浏览器缓存为空 :当我们首次浏览一个页面的时候,真正的对于html文档的请求占用总请求的时间一小部分。其余大部分时间是用于下载其余的所有组件(图片、脚本、样式表、Flash等等)用户的大部分时间也是用于等待组件的下载

浏览器缓存不为空 :如果是第二次浏览该页面,html文档下载时间占比相对来说上升了,因为总的下载时间减少了(由于部分组件得到了缓存的原因,因此不需要再进行下载)。但是大多数时间还是花在了下载组件上面

为啥需要前端性能优化

  • 第一,如果我们可以将 后端响应时间缩短一半,整体响应时间只能减少5%~10%,如果关注前端性能,同样将响应时间减少一半,整体响应时间可以减少40%~45%
  • 通过前端通常只需要较少的时间和资源。减少后端延迟需要大改动,而前端不需要。
  • 前端性能调整已经被证明是可行的,Yahoo!中的团队通过最佳实践降低了响应时间,幅度通常为25%或者更高。

性能黄金法则

只有10%~20%的最终用户响应时间花在了下载HTML文档商,其余80%~90%时间花在了下载页面的所有组件上。

优化方案

方案一:减少HTTP请求(图片地图、CSS Sprites、内联图片和脚本、样式表合并)

根据前面的分析大约80%~90%的时间都是花在了组件的下载上面,因此可以减少组件的数量。但是这个又会和产品设计和引发性能问题相互矛盾。(图片地图、CSS Sprites、内联图片和脚本、样式表合并)这些可以解决这个矛盾。

1、图片地图:

解释:图片地图允许你在一个图片上面关联多个URL,目标URL的选择取决于用户点击了图片的那个位置。(当然可以对每一个链接使用单独的一张图片,但是这样会引发多个http请求,因此使用图片地图只会引发一次http请求,响应时间会降低,减少了HTTP的开销 )。

实例:一个无图片地图的连接

这里写图片描述

实例:一个有图片地图的连接

这里写图片描述

通过比较上述两个页面的加载时间,发现第二个明显时间小于第一个

图片地图包括了服务器端图片地图和客户端图片地图

  • 服务器端图片地图:将所有的点击提交到同一个URL,向其传递用户点击的x和y坐标,Web服务器将x和y坐标映射为合适的操作。
  • 客户端图片地图(更适用):将用户的点击映射到一个操作,无需向后端应用程序发送请求,映射通过HTML的MAP标签实现。

图片地图的缺点:

定义图片上面的坐标,如果采用手动方式,很难完成并且容易出错。而且除了矩形之外无法定义其它的形状。

2、CSS Sprites:

和图片地图一样,CSS Sprites也可以合并图片,但是更为灵活。CSS Sprites是将多个图片合并到一个图片中,CSS Sprites适用于任何支持背景图片的HTML标签中。通过使用CSS的background-position属性可以将元素放置到图片中期望的位置上面。

<style>
    #div1{
        background-image:url('sxs.gif');
        background-position:-260px -90px;
        width:26px;
        height:24px;
    }
</style>
<div id="div1"></div>

实例:一个CSS Sprites的实例

图片地图要求图中的图片必须是连续的,而CSS Sprites没有这个限制。通过合并图片减少了HTTP的请求,并且比图片地图灵活。此外它还降低了下载量,很多人认为合并后的图片比原来的分开的所有图片总和要大,实际上,合并后的图片比分离的图片的总和要小,因为降低了图片自身的开销(颜色表、格式信息等等)

3、内联图片:

通过使用data:URL模式可以在Web页面包含图片但无需任何额外的HTTP请求。data:URL模式在1995年提议的,对它描述为:允许将小块数据内联为‘立即数’,数据就在URL自身中。其它类似的模式包括:ftp:、file:和mailto:。除此之外还有很多模式,smtp:、pop:、dns:等等。

格式:data:[\

方案二:使用内容发布网络(CDN加速)

如果应用程序Web服务器离用户更近,则一个HTTP请求的响应时间将会缩短。另一方面,如果组件Web服务器离用户更近,则多个HTTP请求响应时间将会缩短。 与其重新设计应用程序这一艰巨任务,一遍将应用程序Web服务器分散开,不如先将组件Web服务器分散开,这不仅仅能达到响应时间大幅减少的目的,还很容易实现。

内容发布网络(CDN):

内容发布网络(CDN)是一组分布在不同地理位置的Web服务器,用于更加有效的向用户发布内容。他还能节约成本。向特定用户发布内容的服务器选择基于对网络的可用度测量,例如CDN可能选择网络阶跃数最小的服务器,或者具有最短响应时间的服务器

方案三:添加Expires头

浏览器(和代理)使用缓存来减少HTTP请求的数量,并减小HTTP响应的大小,使得Web服务器页面加载更快。

Expires头:

Web服务器使用Expires头告诉客户端它可以使用一个组件的当前副本,直到指定日期为止。(在这以日期之后,响应应将被认为是无效的)


#响应头中的信息,这是一个有效期非常长久的Expires头,告诉浏览器有效期持续到2014年4月15日

Expries:Mon,15 Apr 2024 20:00:00 GTM

Expires的限制:

Expires头使用一个特定的时间,他要求服务器和客户端的时钟严格同步,另外,过期日期经常检查,并且一旦这一天到来了,还需要在服务器配置中提供一个新的日期

HTTP1.1引入了Cache-Control头来客服Expries头的限制。以秒为单位指定更新窗口。如果组件被请求开始过去的秒数少于max-age,浏览器就使用缓存信息。

可以同时指定Expires和Cache-Control:max-age,两个同时出现,规范指定max-age指令将会重写Expires头


#同时使用

Expries:Mon15 Apr 2024 20:00:00 GTM,
Cache-Control:max-age=3150000

长久的Expires头应该包含任何不经常变化的组件,包括图片、脚本、样式表和Flash组件等,HTML文档不应当使用长久的Expires头,因为可能包含动态内容,每次用户请求都会被更新。

方案四:压缩组件(使用gzip编码压缩HTTP响应包)

通过减小HTTP响应的大小来减少响应时间,如果HTTP请求响应包很小,传输时间就会减少。

使用gzip编码压缩HTTP响应包是最简单的减小页面大小的技术,但是影响最大。(删除注释、缩短URL等都可以减小响应包,但是收效圣微)。

客户端通过HTTP请求的Accept-Encoding表示对压缩的支持,Web服务器看到这个请求头,就会使用其中的一种方法来压缩响应,通过Content-Encoding头通知客户端。压缩通常能够将相应的数据量减少70%。


#请求头

Accept-Encoding:gzip,defate

#响应头

Content-Encoding:gzip

Vary响应头

问题描述:如果一个不支持gzip压缩的浏览器发送第一个请求到代理服务器,代理服务器请求下来的东西没有经过压缩而缓存,这个时候,又有一个支持gzip的浏览器发送了一个相同的请求到代理服务器,代理服务器会使用他的缓存内容,就失去了进行压缩的机会。

解决方法:在Web服务器的响应中添加Vary头,Web服务器告诉代理根据一个或者多个请求头来缓存响应。由于压缩取决于Accept-Encoding请求头,因此需要在服务器的Vary响应头中包含Accept-Encoding

Vary:Accept-Encoding

这样使得代理缓存响应多个版本,为Accept-Encoding请求头的每一个值缓存一份。

使用下面的方式可以禁止代理服务器缓存

Vary:*

#或者

Cache-Control:private

因此对于Web服务器的简单配置,压缩尽可能多的组件,就能显著改善页面的反应速度。

方案五:将样式表放在顶部

将样式表放于文档底部会导致在浏览器中阻止内容逐步呈现。为了避免当样式变化时重绘页面的元素,浏览器会阻止内容的逐步呈现,也就出现了白屏 。白屏是对无样式内容闪烁的弥补,浏览器可以延迟呈现,知道所有的样式表都下载完成后。

规则五对于加载页面实际时间没有太多的影响,影响的是浏览器对这些组件的顺序的反应。

方案六:将脚本放在底部

前面使用样式表的时候,页面逐步呈现会阻止,因此建议将样式表置于HEAD中,使用脚本时,脚本会阻止并行下载,脚本后面的内容,逐步呈现都被阻塞了,后面的组件下载都被阻塞了,将脚本放在页面越靠下的地方,意味着越多的内容能够逐步的呈现。

对页面响应时间影响最大的是,页面组件的数量。HTTP1.1规范建议浏览器从每个主机名并行下载两个组件。因此如果将组件平均放到两个主机中,将会减少一般的时间。

并行下载的优点是很明显的,但是,对于脚本的下载,并行下载实际上是被禁止的,即使用了不同的主机名。

方案七:避免CSS表达式

CSS表达式是动态设置CSS属性的一种强大(并且危险)的方式,受到Internet Explorer5和之后的版本支持。

background-color:expression((new Date()).getHours()%2 ? "#B8D4FF":"#F08A00");
//上面的代码将会使得背景每小时变化一次

expression方法被其它浏览器忽略。

表达式的问题在于对齐进行求值的频率比人们期望的要高,例如在页面宽度使用表达式求值的时候,它不仅仅在页面呈现和大小改变时求值,当页面滚动、甚至用户鼠标在页面上拖拽的时候也会求值。这样当然会影响效率。

避开这个问题:(使用一次性表达式和事件处理器)

一次性表达式: 如果CSS表达式必须被求值一次,那么可以在在这一次执行中重写它自身,如求背景样式就适用这种用样式

<style>
    p{
        background-color:expression(altBgcolor(this));
    }
</style>
<script>
    function altBgcolor(elem){
        elem.style.backgroundColor=(new Date()).getHours()%2 ? "#B8D4FF":"#F08A00";
    }
</script>
<!--上面代码中,CSS表达式调用了altBgcolor()函数,该函数将样式的background-color属性设置为一个明确的值,并移除了CSS表达式。无论在页面发生大小改变、滚动或者是页面上拖拽鼠标(如果有10个p标签),那么智只会执行10次,比原来少了很多。-->

方案八:使用外部JavaScript和CSS

存粹而言,内联(就是把样式表和脚本文件都放在页面中)要快一些。但是将它们外置可以有长期的效益,因为样式表和脚本文件放在外部的话,会有机会缓存的,而html文件特别是那些动态html文件就不会缓存,每次都会请求。

衡量:

页面浏览量:(1)如果每个用户产生的页面浏览量越少,内联的JavaScript和CSS的更快,例如用户每月访问你的网站一次。每次访问之间,外部的javascript和css很可能就被从浏览器缓存中移除了,即使组件可能有长久的Expires头。(2)如果用户能够产生很多的页面,使用外部的javascript和css带来的收益会随着每个用户每月的页面浏览量或者用户每会话的页面浏览量的增加而增加。

组件重用:

如果每个页面都是用了相同的JavaScript和CSS,使用外部组件会提高组件的重用率。

两全其美的办法:(加载后下载、动态内联)

有一些主页,通常每次会只有一个页面浏览量,然而并不是所有的都是这样的,有的会伴随着后续网页的浏览。这个时候,如果作为多次页面浏览量中的第一次的主页,希望主页内联JavaScript和CSS,但又能为所有后续页面浏览量提供外部文件。可以通过在主页加载完成后动态下载外部组件来实现(通过onload事件),这个能够将外部文件放到浏览器缓存中以便用户接下来访问其它页面 。动态内联是通过判断是否存在cookie,如果存在,下一次生成页面的时候,就生成使用外部组件的页面。

加载后下载技术:

<script>
  window.onload = doOnload;
    function doOnload(){
        setTimeout("downLoadComponents()",1000);
    }
    function downLoadComponents(){
        downLoadJs("http://xxxx/xx.js");
        downLoadCss("http://xxxx/xx.css");
    }
    function downLoadJs(url){
        var elem = document.createElement("script");
        elem.src = url;
        document.body.appendChild(elem);
    }
    function downLoadCss(url){
        var elem = document.createElement("link");
        elem.rel = "stylesheet";
        elem.type = "text/css";
        elem.href = url;
        document.body.appendChild(elem);
    }
</script>
<!--首页下载完成后,下载后续的组件-->

方案九:减少DNS查找

DNS查找可以被缓存起来以提高性能。这种缓存可以发生在你的ISP或者局域网中一台特殊的缓存服务器上面,这里探索的是发生在独立用户计算机商的DNS缓存。用户请求了一个主机名之后,DNS信息会保留在操作系统的DNS缓存中(Microsoft Windows上面的“DNS Client”服务,使用ipconfig /displaydns可以查看dns,使用ipconfig /flushdns可以刷新dns),之后对该主机请求,无需过多的DNS查找,至少短时间内不需要。

这里写图片描述

很多浏览器都拥有自己的缓存,缓存中保留了DNS记录,他就不会麻烦操作系统来请求这个记录。只有当浏览器缓存丢弃了记录的时候,他才会向操作系统询问地址。然后操作系统或者通过其缓存来响应这个请求,或者将请求发送给一台远程服务器,这个时候,就会发生潜在的速度降低。

TTL(Time-to-live)

查找返回的DNS记录包含了一个存活时间,Time-to-live值,表示客户端可以对该记录缓存多久。

影响DNS缓存的因素:

服务器可以表明记录被缓存多久,查找返回的DNS包含一个存活时间TTL(time-to-live),尽管操作系统会考虑TTL的值,但是浏览器通常忽略这个值,并设置自己的时间限制。HTTP协议中的Keep-Alive特性可以同时覆盖TTL和浏览器的时间限制。 浏览器对于DNS的缓存数量有限,如果短时间内访问了许多网站,先前的可能丢失,需要再次查找域名,浏览器丢失但是可能操作系统有保留该域名,因此可能无需通过网络发送查询,避免了明显的延迟。

Keep-Alive有重要的作用,默认情况下,一个持久的TCP连接将会一直使用,知道其空闲1分钟为止,由于连接是持久的,无需进行DNS查找。还有一个优点就是,Keep-Alive通过重用现有的连接避免了DNS查找。

减少DNS查找:

  • 减少唯一主机数量:当客户端的DNS缓存为空的时候(浏览器和操作系统都是),DNS查找数量与Web页面中的唯一主机名称数量相等。(减少唯一主机数量,会潜在的减少页面中并行下载的数量)。

方案10:精简JavaScript

精简后可以减少文件的大小,通常的方案有:

  • 删除不必要的注释和空格
  • 混淆:可以应用在源代码上的一种方式,会移除注释和空白,同时还会改写代码(将长的命名替换为短的命名)。

通常精简的工具有JSMin和ShrinkSafe等等

方案11:避免重定向

重定向用于将用户从一个URL重新路由到另一个URL。常用的有301和302等一系列的。

重定向时浏览器会自动将用户带到由Location字段所给出的URL,重定向所需要的所有信息都在这个里面。301和302在实际中通常不会缓存,除非明确指定Expires和Cache-Control控制行为。

重定向还有其他方式将用户重定向到其它URL,例如使用javaScript代码,document.location或者HTML文档头部包含的meta refresh标签可以在其content属性所指定的秒数后重定向用户等等。

<meta http-equiv="refresh" content="0;http://www.baidu.com"/>
<!--包含上面代码的页面,打开后将会经历0s将用户重定向到百度的首页面-->

问题:

重定向引发的延迟也很严重,因为它延迟了整个HTML文档的传输

避免重定向:

  • 缺少结尾的斜线

方案12:删除重复的脚本

方案13:配置ETag

减少呈现页面时所必须的HTTP请求数量是加速用户体验的最佳方式,可以通过最大化浏览器缓存组件的能力实现这一个目标。

ETag

实体标签(Entity Tag,ETage)是Web服务器和浏览器用于确认缓存组件有效性的一种机制。

组件是如何被缓存和确认的

浏览器下载组件后,根据需要会将他们存储到缓存中,后面再次请求组件的时候去检查缓存中的组件是否过期。如果缓存过期了,浏览器在重用之前,必须要检查有效性,这称之为条件GET请求,浏览器必须产生这个请求,但是任然比简单的下载组件效率要高,如果组件是有效的(就是它能够和服务器的组件相互匹配),原始服务器不会返回整个组件,而是返回一个”304 Not Modified”状态码。

服务器检查组件是否和服务器相同的两种方式:

  • 比较最新修订日期Last-Modefied
  • 比较实体标签Etag,HTTP1.1引入的

原始服务器通过Last-Modefied响应头哎返回组件的最新修订日期。


#请求头

GET /i/yahoo.gif HTTP/1.1
HOST:us.yimg.com

#响应头

HTTP 1.1 200 OK
Last-Modefied:tue, 12 Dec 2006 03:03:59 GMT
Content-length:1195

下次请求的时候,会带上If-Modefied-Since头将最新修订日期传回原始服务器进行检查、比较。如果匹配,就会返回304响应。

通过实体实体标签进行,使用ETag的唯一约束就是,必须使用引号将值包起来。


#请求头

GET /i/yahoo.gif HTTP/1.1
HOST:us.yimg.com

#响应头

HTTP 1.1 200 OK
Last-Modefied:tue, 12 Dec 2006 03:03:59 GMT
ETag:"10c24bc-4ab-457e1c1f"
Content-length:1195

下次请求,会使用If-None-Match头将ETag传回原始服务器,如果匹配,就会返回304状态码。

If-None-Match优先级比If-Modefied-Since高

HTTP1.1规范规定的,当同时出现If-None-Match和If-Modefied-Since的时候,则原始服务器禁止返回304(Not Modefied),除非请求中的条件头字段全部一致。

ETag的问题

ETag的问题是通常使用组件的某些属性来构造它,这些属性对于特定的、寄宿了网站的服务器来说是唯一的,但是当浏览器从一台服务器上获得原始的组件之后,又向另外一个服务器发起条件GET请求时,ETag是不会匹配的(对于采用服务器集群来处理请求的网站来说,这是很常见的一种情况),这种会大大降低有效性验证的成功率。

Apache1.3和2.x使用的ETag格式是inode-size-timestamp,文件系统使用inode来存储注入文件类型、文件所有者、组合访问模式等信息。这些信息从一台服务器到另一个服务器,inode是不同的。后面的去掉了inode信息只保留了size大小和timestamp时间戳或者只有timestamp时间戳。

IIS的ETag格式是Filetimestamp:ChangeNumber,ChangeNumber适用于跟踪IIS配置变化的计数器。

方案14:使用Ajax可缓存

改善Ajax请求最重要的方式就是使响应可缓存,前面的规则有一些也适用于Ajax请求。

  • 压缩组件
  • 减少DNS查询
  • 精简JavaScript
  • 避免重定向
  • Etag

其中对于Ajax请求,规则3是最重要的

响应可能会因为数据隐私而不能缓存。当数据被认为是私有的时,大多数都会使用Cache-Control:no-store,使用这个响应头以后,响应不会写入磁盘,但是,对于恶意缓存来说,这一机制,不能确保安全,可能完全忽略掉Cache-Control:no-store头。更安全的方式是使用SSL。

猜你喜欢

转载自blog.csdn.net/lq15310444798/article/details/80375050