网页性能优化

文章首发: http://www.cnblogs.com/sprying/p/4251682.html

本文先从网页性能优化点说起,然后介绍怎么实施性能优化,有哪些性能检测工具。

Yahoo!性能优化35条

Yahoo!性能优化现在有35条,划分成了几个类别,content、server、Cookie、CSS、JavaScript、Images、Mobile。

减少HTTP的请求数

合并Js、CSS文件,使用CSS sprites,使用Inline images(将base64的图片数据放在页面中会加大页面大小,可以放在可缓存的css中),使用iconfont。

CDN

用户80%~90%的时间是用来下载图片、样式、脚本、Flash等静态资源,将静态资源分发到离用户最近的服务器上,可加快下载速度。

使用缓存

某站点设置静态资源缓存后,浏览网页时,一些公用的静态资源已经在浏览其它页面时下载缓存了,不用再发起请求。在缓存过期前,网页内容也没变动时,再次访问网页,所有静态资源都可以从缓存读取。

合理配置缓存策略,在公用的静态资源和请求数之间达到平衡,站点的不同页面间可以共用更多的公用的静态资源。站点更新不仅能即时反馈,而且做到网页只需加载有变动的文件。

http响应头信息Expires、Cache-Control是缓存字段。这里的缓存是指浏览器缓存,缓存过期前不用发起请求。

浏览器中刷新页面,会重新发起所有的请求;如果在地址栏按回车键,可以看到设置缓存的静态资源没有再次发起请求。

Gzip压缩

服务端收到浏览器请求后,经gzip压缩后传输的大小可减小70%,浏览器接收后解压。pdf、图片本身已经压缩了不再需要gzip。一般文档类型默认启用了gzip,其它静态资源,比如样式、脚本要单独配置启用gzip。

将样式表放在页面上方

网页渲染是边下载边解析页面元素,将样式放在页尾,下载完样式后执行会导致页面样式闪烁。

貌似有些浏览器只有等css下载好了之后,才展示出页面。chrome访问过国外网站,出现过css没下载好,整个页面都是空白。

将脚本放在底部

浏览器中JavaScript与UI共用一个线程,现在的浏览器Js下载是并行的(IE8、Firefox3、Chrome2是串行),下载时会阻止页面一些资源加载(google说下载、解释、执行都会阻止),如图片;执行是阻塞浏览器的页面的下载和渲染,所以引入的Js要放在页末。

Ps:

css文件下载是并行的,不阻碍其它文件下载。而Js基本都用来操作Dom的,代码的执行要等Dom渲染完成,所以一般Js代码可以直接放在页尾,如果放在页首,处理Dom元素的Js代码要放在Dom下载完成的事件回调内。

除非你在DOM标签内部指定事件类型和回调函数名(不推荐),不然把Js放在页首没意义。

避免使用CSS中的Expressions

1
background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );

 这种写法只有IE支持,浏览器不仅在渲染页面或者改变页面大小时触发,甚至在滚动、移动鼠标时也触发,统计发现操作了一次浏览器产生10000个evaluations。

将JavaScript和CSS独立成外部文件

实现结构、样式、行为分离,独立成文件可以利用浏览器缓存。

压缩 JavaScript 和CSS

压缩JavaScript时,如果使用模块化开发时,比如seajs不能直接压缩,要先transport下,具体可参考spm的grunt-cmd-transport插件

避免重定向

Request URL:http://yun.ys7.com/gc.html
Request Method:GET
Status Code:301 Moved Permanently (from cache)

Response Headers
Content-Length:178
Content-Type:text/html
Date:Tue, 03 Feb 2015 15:34:42 GMT
Location:http://i.ys7.com/square/index.jsp
Server:nginx 

重定向的http请求返回301或者302,返回的报文的body部分是空的。

上面是访问http://yun.ys7.com/gc.html,服务器告诉浏览器重定向到//i.ys7.com/square/index.jsp,浏览器重新发起//i.ys7.com/square/index.jsp请求。

浏览器访问网页,通过重定向而发起两次请求,影响性能。

剔除重复的脚本

团队规模越大、代码越多,越有可能出现;如果出现,会导致没必要的请求和Js运算

配置ETags

etag判断一个静态资源有没有更新。请求一个静态资源,返回的头信息有etag字段,再次请求时,通过头信息中If-None-Match字段将etag值带过去,与服务端的文件etag值比对,如果一致就直接返回状态码 304,浏览器使用缓存中的静态资源;不一致,服务器将最新文件传输过来。 etag值很重要,能反应出资源有无更新。一般的etag值inode-size-timestamp,虽然不同的服务器上inode值不一样。 这样如果静态资源部署在多台服务器上的,用etag就有问题。关闭etag,将判断一个静态资源有没有更新就交给Last-Modified。

使用AJAX缓存

使用get类型的ajax请求,后台处理接口时,加上http头信息Expires,以后浏览器再次发起请求时,在缓存的时间内,直接从浏览器缓存中读取。

比如有个网页联系人列表不经常变动,ajax请求联系人列表接口的url带上资源标志id(id是页面初始化时由后台带回来,后台根据联系列表生成,如果列表变化了,id也变化),第一次访问时,ajax调用接口缓存了;第二次调用时,如果联系人列表没有变动过,id还是不变,url也不会变,直接从缓存读取。

尽早刷新输出缓冲

后台生成一个完整的页面,要经历数据库查询,一系列业务流程,假设耗时为A,再由服务器容器转化成html页面返回给浏览器。浏览器边下载边解析DOM,下载页面相关的静态资源,假设耗时为B,整个耗时就是(A+B)。 如果A和B并行呢? 服务器容器先将静态资源的相关内容返回给浏览器,服务器一边进行查询等耗时操作(A),浏览器同时一边解析DOM中到引用静态资源,就开始下载(B)。 目前php支持。

Example:

... <!-- css, js -->
</head>
<?php flush(); ?>
<body>
... <!-- content -->

使用GET来完成AJAX请求

使用post请求,浏览器要进行两个过程,先发头信息,再发送数据。而使用get,请求数据都在url上,只需发一次,IE下最大长度为2k,从语义化角度,来说,get是获取数据的意思。get请求会被缓存,post不会,post传输比较安全。

推迟加载内容

将所有静态资源都下载好了之后,再执行Js渲染等操作,会影响首屏内容最快显示。在模块化开发时,当用到插件效果时去下载,或者等浏览器加载的标签转圈圈好了之后,触发load之后再执行其它下载,使用require.async延迟加载。

预加载

在页面空闲时,预加载接下来操作可能需要的资源。有些网站改版时,第一次加载要加载许多新的静态资源,导致用户体验很差,可以在改版前预加载新的静态资源。

减少DOM元素数量

将静态资源划分到不同的静态服务上 不同浏览器对同一域名的并发数如下,将域名数控制在2-4个。

使iframe的数量最小

耗性能,即使是blank;阻塞父页面的onload。

不要出现404错误

404请求会阻碍并行下载,并且浏览器可能去尝试解析404返回内容发现有用的东西。

减小Cookie大小

cookie在每次http请求会带到服务端,cookie越大,传输的数据量越大。

如果站点域名是www.demo.com,静态资源放在s.demo.com域名下,请求静态资源时会将.demo.com下的cookie带过去。 要做到free cookie,可以申请个单独的域名,比如domestic.com,拆分几个二级域名,s0.demostatic.com,s1.demostatic.com。

减少DOM访问

Dom操作时,js要通过js-dom桥访问dom,速度比js内部操作慢,所以要减少DOM操作。写代码时,

1)缓存指向dom的引用

2)离线操作节点,最后才更新到DOM中

3)避免Js操作一些属性导致浏览器的页面重绘

优化事件绑定

采用事件委托,避免重复绑定;如果操作DOM,事件回调绑定在DOMContentLoaded,避免使用onload。

无法使用并行下载

避免使用滤镜

IE下使用AlphaImageLoader filter来解决IE<7不支持透明,在下载图像过程中,使用这个属性会阻塞浏览器,并且会加大内存消耗。避免使用而用png8代替,如果实在要使用AlphaImageLoader,用_filter。

优化图像

优化图像大小

优化CSS Spirite

不要在HTML中缩放图像

favicon.ico要小而且可缓存

保持单个内容小于25K(针对手机)

iPhone手机最大能缓存25k,ios3有这个限制,但是有人在ios7上用css测试3Mb的文件,缓存了没发起请求。

打包组件成复合文本(针对手机)

手机端请求是很昂贵的,打包成一个复合文本,减少请求数。但先要检测终端是否支持。

避免img标签的src为空

两种情况下会发生

1
2
3
4
<img src=“”>
  
var img = new Image();
img.src="";

浏览器可能发起请求当前页面所在目录,或者当前页面,引起没必要的麻烦,比如影响日志统计,服务端重新生成页面,浏览器重新接收引起的数据丢失。

其它优化点

DNS域名解析加速

咨询了运维,目前万网已经支持域名解析加速,可以向com推送变更,1分钟内生效,各省的域名解析服务器目前应该有双向能力。

Http请求要保持Keep-Alive

获取静态资源之前要建立通道,如果每次获取静态资源都要建立通道,会加大传输时间。

图片要声明width和height

尽快渲染出,避免重排与重绘。

使用webp格式图片

最大可以将图片减小到原图片的1/3大小,是google制定了,其它浏览器厂商并不吃google的菜,只有chrome支持,直接在网页保存webp格式的图片,本地不能预览,目前淘宝商品详情页采用这种格式图片。

document.write

不要在外链中使用document.write加载资源

文档编码类型

doc 返回编码charset=uft-8,加快解析;不要在html中设置,貌似ie8下有bug。

使用MXHR

将css、图片、js资源整合成一个文件请求,程序后台使用分隔符将这些数据分隔开来,然后在前台拆分;还可以readystatus=3边传输边解析。缺点是,不能缓存;老版本的IE不支持readystatus =3和data:url,在Ie8中就可以了,必须在Ie6、7下设法变通。觉得在淘宝,浏览商品许多图片时,用这个不错,不过淘宝商品的图片,不在当前发起请求文档的同个域名下,有跨域问题吧

无阻塞的脚本

将脚本放在页面底部是Yahoo!性能优化的优化点,其实,是解决脚本下载与执行是阻塞问题。如果使用无阻塞的脚本呢? Js引擎和UI线程公用一个线程,无阻塞引入脚本,可解决在引入js时,阻塞UI渲染。

1) 在script标签加入defer属性

当页面解析到script标签时,开始下载Js文件,页面并不等待继续解析,不会阻碍其它页面资源的下载,由于只有 Explorer 4+ 和 Firefox 3.5+支持,这里就不详细讨论。

2) 动态脚本注入(dynamic script tag insert)

在body结束标签处,通过Js的Dom操作,新建script元素,设置好src之后,添加到head中(最好是head,body中可能),就开始下载,不会影响浏览器UI线程,下载完触发script标签的onload事件(不同浏览器有差异),如果创建多个script后并行下载,有可能后增的Js先下载完成,有的浏览器会等待,比如FireFox、Opera,有的先下载完先执行。对于有依赖关系的Js文件,可以在前一个Js下载完成后,再加载新的Js。目前豆瓣的do.js、in.js都根据这个原理实现的封装。

3) XMLHttpRequest注入

在项目里,我们俗称ajax,请求的路径改为一个js的url,在返回成功的回调里面,进行Dom操作,创建一个script,将请求得到的响应报文,设置为scritpt的dom对象的text属性。 评析:不能请求跨域的js,设置text属性后,立马就执行Js,所以通过控制设置text属性的时机,来控制Js的执行。不过这种方法还是被很少用,可能是将js内容直接填充到html页面的script元素中,总人本能觉得不整洁吧。

优化数据传输

传输数据的几种方式

xhr jsonp 获取信息,json-padding,基于动态脚本注入,支持跨域 Beacons 图片信标,简单效率高,一般用来发送页面统计信息

数据的传输格式

xml、Json、拼接的字符串、json-p

数据的本地存储

使用ajax缓存、localstorage

提升Js代码的执行效率

缓存DOM节点查找的结果

用jquery选择器选中的元素,如果还需第二次使用,建议缓存到变量中,避免重复的创建jQuery对象。

用hash-table来优化查找

在查找元素时,使用对象比使用数组更有效,使用数组还需要遍历,但对象不会保持默认的排序。

用innerHTML代替DOM操作,减少DOM操作次数

dom操作是有代价的,访问它

实践性能优化

有些性能优化点,需要前端工程师在编码过程中遵守;有些需要配置服务器,设置缓存、etag、gzip;ajax缓存需要后台提供接口时设置;一些关键的优化点,需要前端工程支持。

前端工程化支持

在模块化开发时,细分了许多小文件,如何合并压缩脚本、样式,并在多页面中利用好缓存,如何使css spirites、inline images用起来很easy。如何保证将样式表放在页面上方、将脚本放在底部、剔除重复的脚本。

nginx缓存设置

expires 浏览器本地缓存设置

语法:expires [time|epoch|max|off]
默认值:expires off
使用字段:http, server, location

off 将禁止修改头部中的 Expires和Cache-Control字段。
Time控制“Cache-Control”的值,负数表示no-cache
epoch 将Expires头设置为1 January, 1970 00:00:01 GMT。
max 将Expires头设置为31 December 2037 23:59:59 GMT,将Cache-Control最大化到10 年。
expires 30d;
expires 1h;
 

nginx的gzip设置

nginx 在http{….}两个大括号之间,配置gzip段如下:

1
2
3
4
5
6
7
gzip on;
gzip_min_length 1k;
gzip_buffers 16 64k;
gzip_http_version 1.1;
gzip_comp_level 6;
gzip_types text/plain application/x-javascript text/css application/xml;
gzip_vary on;

 

性能检测工具

性能监测工具我划分为3类,一类检测规范的遵循情况,一类检测Js的执行效率,还有监控网页的加载速度,如首屏加载时间。

类型一

可为项目后续优化提供参考。分数低,不一定网页首屏加载慢。

  • yslow(Firfox、chrome的插件)

  按照yahoo!的网页性能优化标准来评分。

    技术分享

  我在用yslow过程中,发现可惜的是,动态脚本注入的,没有统计到。

 

  • pagespeed

  参照google网页性能优化标准来评测,下面是一个网站的评测

  技术分享

 

  如避免重定向、压缩图片、指定缓存验证工具、启用 Keep-Alive等这些建议在yslow中没有的,所以跑完yslow,可以再跑下pagespeed。

类型二

  • DynaTrace Ajax Edition

  需要本地安装,目前只支持IE,firefox。工具除了提供常规的性能优化建议外,还统计了Js方法的执行时间。

    技术分享

  上面的数据,是使用seajs.use后,下载完依赖模块文件,js执行耗时相关信息,可以一层层定位,最后定位到是哪个函数执行比较耗时,比如过多使用append。

  • speed tracer

  google的另外一套工具,speed tracer,追踪加载过程,以图形化方式展示,有执行时间,可以定位到Js代码,但一般都定位到封装库的源码中。

    技术分享

类型三

  • 基调

  从分布在各省市、运营商的节点,测试出不同的系统、浏览器对同一网页的加载性能、首屏时间、脚本报错统计;更加接近用户角度。需要购买服务。

  下面是某站点一周的首屏加载时间统计

  技术分享

  

  下面是即时监测某网页的一些数据

  技术分享

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。