• 前端性能优化总结


    写在前面:

       这里只是简单总结了一些,有些并未详细展开,具体可以看《高性能网站建设》这本书。

    目录:

    • 减少HTTP请求(图片地图,雪碧图,dataurl,合并脚本和样式表)
    • 使用内容发布网络CDN
    • 添加Expires头(缓存)
    • 压缩组件

    一、减少HTTP请求

    http请求越多,那么消耗的时间越多,如果在加上网络很糟糕,那么问题就更多了。且如果网页中的图片、css文件、js文件很多甚至有音乐文件时,这势必会造成负担。

    方法一: 图片地图

      即多个图片排成一行作为链接到其他页面的按钮,我们当然可以使用五福图片,发送5个http请求,但是这是不合适的。

      我们可以选择使用图片地图,即只用一张图片,然后使用<map>属性通过控制坐标来实现:

    •  http://stevesouders.com/hpws/imagemap-no.php  这是没有使用图片地图的实例。
    •    http://stevesouders.com/hpws/imagemap.php 这是使用了图片地图的例子,速度明显加快。

      优点: 大幅加快加载速度,减少http请求;   缺点: 手工设置坐标非常麻烦,在IE下支持的不好。

    方法二CSS Sprite(可行)

        即一个网页上有很多的小图片,比如有20个,如果我们都请求一遍,就需要使用20个http请求,这是很耗时的。

        但是我们可以把这些图片合成一个大的图片,然后将之作为 background-img插入进去, 根据不同的图片设置不同的background-postion即可,网易等就是采取的这种做法。

        说明:虽然在不同的位置需要请求很多的图片,但是,实际上我们查看网络只会请求一次。

                  优点: 速度快,可以和图片地图相比拟。缺点:计算坐标比较麻烦,做图需要额外花费时间,且图片后期维护会死人,只能是独立图片。

             使用场景: 一些很少改变的图片(静态的),如背景、按钮、导航栏、连接等。

    方法三:内联图片(Data URL)

         使用data:url的方式来内联图片,它不需要额外的http请求

                   Data URL给了我们一种很巧妙的将图片“嵌入”到HTML中的方法。跟传统的用img标记将服务器上的图片引用到页面中的方式不一样,在Data URL协议中,图片被转换成base64编码的字符串形式(Base64就是一种基于64个可打印字符来表示二进制数据的方法,可用于在HTTP环境下传递较长的标识信息),并存储在URL中,冠以mime-type。

                  既然base64不需要额外的http请求,那么问题来了,如何将图片转化成base64编码呢?——FileReader对象的readAsDataURL方法可以将读取到的文件编码成Data URL。核心部分代码如下所示:

    var reader = new FileReader(), htmlImage;
    reader.onload = function(e) {
        htmlImage = '<img src="'+ e.target.result +'" />';    // 这里e.target.result就是base64编码
    }
    reader.readAsDataURL(file);
    1. Data URL基本原理

           图片在网页中的使用方法通常是下面这种利用img标记的形式:

      <img src="images/myimg.gif ">  

      这种方式中,img标记的src属性指定了一个远程服务器上的资源。当网页加载到浏览器中时,浏览器会针对每个外部资源都向服务器发送一次拉取资源请求,占用网络资源。大多数的浏览器都有一个并发请求数不能超过4个的限制。这意味着,如果一个网页里嵌入了过多的外部资源,这些请求会导致整个页面的加载延迟。而使用Data URL技术,图片数据以base64字符串格式嵌入到了页面中,与HTML成为一体,它的形式如下:
     
    <img src="data:image/gif;base64,R0lGODlhAwADAIABAL6+vv///yH5BAEAAAEALAAAAAADAAMAAAIDjA9WADs=" />

      从上面的base64字符串中你看不出任何跟图片相关的东西,但下面,我们将传统的img写法和现在的Data URL用法左右对比显示,你就能看出它们是完全一样的效果。但实际上它们是不一样的,它们一个是引用了外部资源,一个是使用了Data URL。

      几乎所有的现代浏览器都支持Data URL格式,包括火狐浏览器,谷歌浏览器,Safari浏览器,opera浏览器。IE8也支持,但有部分限制,IE9完全支持。

    2. Data URL的优缺点

      Data URL能用在很多场合,跟传统的外部资源引用方式相比,它有如下独到的用处:

    • 当访问外部资源很麻烦或受限时(这个比较鸡肋)
    • 当图片是在服务器端用程序动态生成,每个访问用户显示的都不同时(场景较少)
    • 当图片的体积太小,占用一个HTTP会话不是很值得时(雪碧图可以出场了)

      Data URL也有一些不适用的场合

    • Base64编码的数据体积通常是原数据的体积4/3,也就是Data URL形式的图片会比二进制格式的图片体积大1/3
    • Data URL形式的图片不会被浏览器缓存,这意味着每次访问这样页面时都被下载一次。这是一个使用效率方面的问题——尤其当这个图片被整个网站大量使用的时候
    • 浏览器支持问题,使用base64编码图片作为背景图片的这种技术IE6/IE7浏览器是不支持的(IE9浏览器IE7模式下支持)
    3. 在CSS里使用Data URL

      我们无法否认缓存在浏览器性能中的重要作用——如何能将Data URL数据也放入浏览器缓存中呢?答案是:通过CSS样式文件。CSS中的url操作符是用来指定网页元素的背景图片的,而浏览器并不在意URL里写的是什么——只要能通过它获取需要的数据。所以,我们就有了可以将Data URL形式的图片存储在CSS样式表中的可能。而所有浏览器都会积极的缓存CSS文件来提高页面加载效率。

      假设我们的页面里有一个很小的div元素,我们想用一种灰色的斜纹图案做它的背景,这种背景在当今的网站设计者中非常流行。传统的方法是制作一个3×3像素的图片,保存成GIF或PNG格式,然后在CSS的background-image属性中引用它的地址。而Data URL则是一种更高效的替代方法,就像下面这样。

    background-image: url("data:image/gif;base64,R0lGODlhAwADAIAAAP///8zMzCH5BAAAAAAALAAAAAADAAMAAAIEBHIJBQA7");  

          这个方法避免了让这个小小的背景图片独自产生一次HTTP请求,而且,这个小图片还能同CSS文件一起被浏览器缓存起来,重复使用,不会每次使用时都加载一次。因此,在以下四种情况同时存在的情况下,有base64编码作为background-image背景图片利要远大于弊:

    • 这类图片不能与其他图片以CSS Sprite的形式存在,只能独行
    • 这类图片从诞生之日起,基本上很少被更新
    • 这类图片的实际尺寸很小
    • 这类图片在网站中大规模使用

        使用base64编码代替CSS背景图片是有局限性的,并不是所有图片都适合使用base64编码这种技术的。例如:
          1. CSS Sprite图片后期维护会死人,只能是独立图片
          2. 图片尺寸过大,CSS文件就会变成了臃肿的大棒子,反而不利于加载
          3. CSS文件的优点就是重用,因此,如果背景图片就一个地方使用,减少的请求数有限,考虑到其他成本,还不如直接使用普通url图片地址
          4. 如果图片经常改动,好吧,苦逼的前端加班仔中就多了一个你

    方法四:合并脚本和样式表(可行)

        一般来说将css和js放在外部文件中会比内联更好一些,但是如果按照软件工程师所推荐的方式和模块化的原则将代码分开放到不同的多个小的文件中,会降低性能,因为每个文件都会导致一个额外的http请求。

        为了清晰,我们不建议将脚本和样式表合并在一起,但是多个脚本应该合并为一个脚本,多个样式表应该合并为一个样式表。

    •   http://stevesouders.com/examples/combo-none.php 这个是分离脚本的示例
    •     http://stevesouders.com/examples/combom.php 这个是合并脚本的示例

           如果你是进行了js的模块化开发,可能觉得合并是一种倒退,但是我们可以使用在生成过程中从一组特定的模块生成一个目标文件,如使用webpack构建工具。

    二、使用内容发布网络(CDN)

           CDN即 content distribute network,内容分发网络。其原理是---尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。

           下面的这个例子可以很好的说明问题:古代打仗大家一定都知道,由于古代的交通很不发达,所以当外族进攻的时候往往不能及时的反击,等朝廷征完兵再把兵派往边境的时候那些侵略者却是早已不见了踪影,这个让古代的帝王很是郁闷。后来帝王们学聪明了,都将大量的兵员提前派往边境驻扎,让他们平时屯田,战时当兵,这样的策略起到了很显著的作用。

      曾经一个网站的所有服务器都只在一个地方,但是现在不同了,因为我们知道http请求是耗时的,如果服务器用户更近,请求的时间不久更短了吗? 所以可以将服务器置于多个分散的地方,而使用cdn就可以保证我们每次从最近的地方获取到响应。

      说明1: CDN只是用来提供静态内容的,如图片、脚本、样式表和Flash。提供动态html页面会引入特殊的存储需求 - 数据库连接、状态管理、验证、硬件和OS优化等。

      说明2: 你所测试的CDN速度与你所处的位置、服务器位置都有很大的关系。

      优点: 响应速度更快(大部分时间是这样的);

      缺点1: 依赖CDN使得你的响应时间会受到其他网站-甚至是竞争对手网站流量的影响。因为CDN服务器提供商在其所有的客户之间共享其Web服务器。

      缺点2: 无法直接控制组件服务器所带来的特殊麻烦。  如,修改HTTP响应头必须通过服务提供商来完成,而不是你的工作团队完成。  

    •   http://stevesouders.com/hpws/ex-cdn.php  提供了cdn 
    •       http://stevesouders.com/hpws/ex-nocdn.php  无cdn   (本机测试,cdn多数情况下更快一些~)

     

    三、添加Expires头(缓存)

           在设计web页面的时候,首次访问的响应时间并不是唯一需要考虑的。 如果是这样的话,我们可以将规则1发挥到极致,并且不再页面上放置任何图片、脚本和样式表。 然后,我们知道,这些可以增强用户体验,这也就意味着需要更长的时间来加载这些东西。

      虽然在第一次访问的时候需要进行很多的http请求,但是通过一个长久的Expires头,可以使得这些组件被缓存,这样在后续的请求中可以避免很多不必要的http请求,需要添加的包括图片、css、脚本

      http://www.cnblogs.com/zhenguoli/p/8847938.html 有关缓存的相关知识可以参考这篇文章(包括url中回车、f5和ctrl+f5的区别)

                 其中:url是先看有没有过期,如果没有过期就用缓存,如果过期了就去服务器询问一下请求的内容是否发生改变

                           f5是不管有没有过期都要去服务器询问一下

                           ctrl+f5是不管怎么样都不要现在的,直接全部从服务器获取文件

                           【ctrl + shift + del  清空缓存】

      注意: Expires的缺点是必须保证浏览器和服务器的时间严格一致,否则就会出现问题。

      注意: 只有在用户已经访问了你的网站之后,长久的Expires头才会对页面浏览产生影响。 当用户第一次访问你的网站的时候,它不会对HTTP请求的数量产生任何影响。故其性能的改进取决于用户在访问你的页面的时候是否有完整的缓存。

      为图片使用长久的Expires头非常普遍, 但是这一最佳实践不应当仅仅用在图片上,长久的Expired头应该包括任何不经常变化的组件,包括脚本、样式表和Flash组件。但是HTML文档不应该使用长久的Expires头,因为他包含动态内容,这些内容在每次用户请求时都将不断刷新。

      那些基本上不需要更新的内容我们就直接缓存即可,缓存之后我们就认为这是静态页面了。如:govclouds.cn 他的图片、大部分的css和js都是缓存的,因为大多数都不需要改变。

      另外,目前的很多网站的形式都是类似于www.sina.com.cn即一个页面是详情页,然后其中还有插入的推荐页面(不断变化的)、还有用于收费广告的页面(需要不断变化),这些动态变化的页面大多数都是使用iframe嵌入的,而基本不变的详情页就使用了缓存,动态变化的iframe不会使用缓存。(有关iframe的优缺点可参考这篇文章:https://www.cnblogs.com/wyhlightstar/p/7066153.html

      而对于一些可能很快就要改变的,例如展示即时的新闻的页面,其图片一般就不会被缓存。如cnn.com,它对于图片的缓存就只会缓存网站的图标(基本上不会改变)和网站的一些背景图片,而对于其他内容就不会改变。

      下面的两个例子分别是无Expires的示例和有Expires的示例:

    •  http://stevesouders.com/hpws/expiresoff.php   无Expires的示例
    •  http://stevesouders.com/hpws/expiresoff.php   有Expires的示例

      通过测试可以发现,添加了Expires头的示例的速度第一次比较慢,但是后面可以缩短一半的时间。 而前者一直都非常慢!效果非常显著。

    四、压缩组件

            使用减小文件体积的压缩已经在E-mail和FTP站点中使用了10年,目前HTTP1.1也是开始支持了。其中,web客户端可以通过HTTP请求中的Accept-Encoding来表示对压缩的支持,如下所示:

       

      显然,只要是同一个客户端,都会发送这样的请求,其中,gzip是免费的、标准的压缩形式,而deflate用的则非常少。 

      服务器端接收到这样的请求之后,如果服务器将一个文件压缩就会在响应的时候包含下面的字段表示对之进行了压缩,如下所示:

          

      一般,大多数的网站都会压缩hTML、CSS和js脚本,通常不会压缩图片和PDF,因为他们本来就是已经被压缩过的。

      另外,压缩是有成本的,因为服务器需要花费额外的CPU周期来完成对文件的压缩, 并且当压缩文件响应到了客户端时,客户端需要进行解压缩,这都要时间,如果说耗费的时间还不如节省的时间,那就不要压缩了。  美国前十的网站有9个都在压缩HTML、而css和js则是选择性的。通过压缩,可以将响应的额外的数据量较少将近70%。

      我在地址栏输入www.aol.com的时候,查看网络信息如下:

      可以看到,它第一次发送的请求是 http: //www.aol.com/ ,响应状态码为301(Moved Permanently),即永久移除,这时候响应头中就会包含 Location 字段,然后浏览器就会根据服务器发送回来的新的地址,重新访问,于是产生了第二条请求,如下所示:

      即,这次浏览器利用服务器响应的Location字段的值来重新发送了请求, 这次就发送成功了。 即200 Connetion established 。  即建立了TCP建立了连接。

      而如果我们第一次就输入 https://www.aol.com/  这样就不会发生301的情况了,而是200OK。

      有时候我们可以看到在响应头中含有vary字段,如www.aol.com中的,如下所示:

      

      vary: “Accept-Encoding”;是什么意思呢?

      因为当浏览器直接和服务器进行通信时,迄今为止所有的介绍的配置都能很好的工作,但是当浏览器通过代理来发送请求时,情况就变得复杂了会造成严重的问题。

      解决方法就是在weg服务器的响应头中添加vary头。 即web服务器告诉代理相关信息。

      下面的例子是未压缩、只压缩HTML和压缩所有组件的例子:

    • http://stevesouders.com/hpws/nogzip.html     这是没有压缩的例子。
    • http://stevesouders.com/hpws/gzip-html.html     这是只压缩html的例子。
    • http://stevesouders.com/hpws/gzip-all.html     这是压缩所有组件的例子。

      相对于不压缩,压缩html可以节省约为9.7%的时间(当然不是绝对的),压缩所有组件可以节约约为53.2%的时间。

      不压缩的响应头:

      

      压缩了html文件的响应头:

      实际测试时,感觉响应的更快(是原作者将一个js文件也误压缩了)

      

      压缩了所有组件的响应头:

      根据content-type也可以知道这是一个js文件。

      总结:通过比较,可以发现经过压缩,响应的时间得到了很明显的提高。

    五、 使用link将样式表放在顶部

    六、 将脚本放在底部

    七、避免使用css表达式

    八、使用外部的JavaScript和CSS

    九、减少DNS查找

            我们知道一次DNS的解析过程会消耗20-120毫秒的 时间,在dns查询结束之前,浏览器不会下载该域名下的任何东西。所以减少dns查询的时间可以加快页面的加载速度。因此我们建议一个页面所包含的域名数尽量控制在2-4个。这就需要对页面整体有一个很好的规划。

            还可以使用dns-prefetch,俗名叫dns预获取,主要作用是减少请求次数和提前对dns预获取,简单的说你的站点域名是x.com,而你网站上一些的图片等资源是放在img.x.com上,每次请求会涉及到dns解析问题,对于性能要求极致的站点这个处理就必不可少,在head中加入<link rel="dns-prefetch" href="//img.x.com">就能达到上述的效果,需要注意,虽然dns-prefetch能够加快网页解析速度,但是也不能随便滥用,因为多页面重复DNS预解析会增加重复DNS查询的次数。

    十、 精简JavaScript

    十一、避免重定向

  • 相关阅读:
    窗体吸附 Timer + 判断Location (简单实用)
    C# FTP 应用程序
    C# 加密方法汇总
    LINQ 标准的查询操作符 合计操作符 Count()、Sum()、Min()、Max()、Average()和Aggregate()
    委托中的协变和逆变(C# 编程指南)
    深入探讨C#序列化和反序列化
    grep 命令详解
    Oracle 数据库的启动和关闭的方式!
    linux 下的光盘拷贝
    C3P0连接池配置
  • 原文地址:https://www.cnblogs.com/zhenguoli/p/8846761.html
Copyright © 2020-2023  润新知