网页性能优化
文章首发: 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
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越大,传输的数据量越大。
对静态资源使用无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。
不要使用@import 用link
无法使用并行下载
避免使用滤镜
IE下使用AlphaImageLoader filter来解决IE<7不支持透明,在下载图像过程中,使用这个属性会阻塞浏览器,并且会加大内存消耗。避免使用而用png8代替,如果实在要使用AlphaImageLoader,用_filter。
优化图像
优化图像大小
优化CSS Spirite
不要在HTML中缩放图像
favicon.ico要小而且可缓存
保持单个内容小于25K(针对手机)
iPhone手机最大能缓存25k,ios3有这个限制,但是有人在ios7上用css测试3Mb的文件,缓存了没发起请求。
打包组件成复合文本(针对手机)
手机端请求是很昂贵的,打包成一个复合文本,减少请求数。但先要检测终端是否支持。
避免img标签的src为空
两种情况下会发生
<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 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段如下:
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代码,但一般都定位到封装库的源码中。
类型三
- 基调
从分布在各省市、运营商的节点,测试出不同的系统、浏览器对同一网页的加载性能、首屏时间、脚本报错统计;更加接近用户角度。需要购买服务。
下面是某站点一周的首屏加载时间统计
下面是即时监测某网页的一些数据
参考文章
https://developer.yahoo.com/performance/rules.html
其它文章
16毫秒的优化——Web前端性能优化的微观分析-->从js角度来分析
*******站在巨人的肩膀上