文章首发:http://www.cnblogs.com/sPRying/p/4251682.html
本文先从网页性能优化点说起,然后介绍怎么实施性能优化,有哪些性能检测工具。
Yahoo!性能优化现在有35条,划分成了几个类别,content、server、Cookie、CSS、javaScript、Images、Mobile。
合并Js、CSS文件,使用CSS sprites,使用Inline images(将base64的图片数据放在页面中会加大页面大小,可以放在可缓存的css中),使用iconfont。
用户80%~90%的时间是用来下载图片、样式、脚本、Flash等静态资源,将静态资源分发到离用户最近的服务器上,可加快下载速度。
某站点设置静态资源缓存后,浏览网页时,一些公用的静态资源已经在浏览其它页面时下载缓存了,不用再发起请求。在缓存过期前,网页内容也没变动时,再次访问网页,所有静态资源都可以从缓存读取。
合理配置缓存策略,在公用的静态资源和请求数之间达到平衡,站点的不同页面间可以共用更多的公用的静态资源。站点更新不仅能即时反馈,而且做到网页只需加载有变动的文件。
http响应头信息Expires、Cache-Control是缓存字段。这里的缓存是指浏览器缓存,缓存过期前不用发起请求。
浏览器中刷新页面,会重新发起所有的请求;如果在地址栏按回车键,可以看到设置缓存的静态资源没有再次发起请求。
服务端收到浏览器请求后,经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放在页首没意义。
background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );
这种写法只有IE支持,浏览器不仅在渲染页面或者改变页面大小时触发,甚至在滚动、移动鼠标时也触发,统计发现操作了一次浏览器产生10000个evaluations。
实现结构、样式、行为分离,独立成文件可以利用浏览器缓存。
压缩JavaScript时,如果使用模块化开发时,比如seajs不能直接压缩,要先transport下,具体可参考spm的grunt-cmd-transport插件
Request URL:http://yun.ys7.com/gc.htmlRequest Method:GETStatus Code:301 Moved Permanently (from cache)
Response HeadersContent-Length:178Content-Type:text/htmlDate:Tue, 03 Feb 2015 15:34:42 GMTLocation:http://i.ys7.com/square/index.jspServer:nginx
重定向的http请求返回301或者302,返回的报文的body部分是空的。
上面是访问http://yun.ys7.com/gc.html,服务器告诉浏览器重定向到//i.ys7.com/square/index.jsp,浏览器重新发起//i.ys7.com/square/index.jsp请求。
浏览器访问网页,通过重定向而发起两次请求,影响性能。
团队规模越大、代码越多,越有可能出现;如果出现,会导致没必要的请求和Js运算
etag判断一个静态资源有没有更新。请求一个静态资源,返回的头信息有etag字段,再次请求时,通过头信息中If-None-Match字段将etag值带过去,与服务端的文件etag值比对,如果一致就直接返回状态码 304,浏览器使用缓存中的静态资源;不一致,服务器将最新文件传输过来。 etag值很重要,能反应出资源有无更新。一般的etag值inode-size-timestamp,虽然不同的服务器上inode值不一样。 这样如果静态资源部署在多台服务器上的,用etag就有问题。关闭etag,将判断一个静态资源有没有更新就交给Last-Modified。
使用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延迟加载。
在页面空闲时,预加载接下来操作可能需要的资源。有些网站改版时,第一次加载要加载许多新的静态资源,导致用户体验很差,可以在改版前预加载新的静态资源。
不同浏览器对同一域名的并发数如下,将域名数控制在2-4个。
耗性能,即使是blank;阻塞父页面的onload。
404请求会阻碍并行下载,并且浏览器可能去尝试解析404返回内容发现有用的东西。
cookie在每次http请求会带到服务端,cookie越大,传输的数据量越大。
如果站点域名是www.demo.com,静态资源放在s.demo.com域名下,请求静态资源时会将.demo.com下的cookie带过去。 要做到free cookie,可以申请个单独的域名,比如domestic.com,拆分几个二级域名,s0.demostatic.com,s1.demostatic.com。
Dom操作时,js要通过js-dom桥访问dom,速度比js内部操作慢,所以要减少DOM操作。写代码时,
1)缓存指向dom的引用
2)离线操作节点,最后才更新到DOM中
3)避免Js操作一些属性导致浏览器的页面重绘
采用事件委托,避免重复绑定;如果操作DOM,事件回调绑定在DOMContentLoaded,避免使用onload。
无法使用并行下载
IE下使用AlphaImageLoaderfilter来解决IE<7不支持透明,在下载图像过程中,使用这个属性会阻塞浏览器,并且会加大内存消耗。避免使用而用png8代替,如果实在要使用AlphaImageLoader,用_filter。
优化图像大小
不要在HTML中缩放图像
iPhone手机最大能缓存25k,ios3有这个限制,但是有人在ios7上用css测试3Mb的文件,缓存了没发起请求。
手机端请求是很昂贵的,打包成一个复合文本,减少请求数。但先要检测终端是否支持。
两种情况下会发生
<img src=“”>var img = new Image();img.src="";
浏览器可能发起请求当前页面所在目录,或者当前页面,引起没必要的麻烦,比如影响日志统计,服务端重新生成页面,浏览器重新接收引起的数据丢失。
咨询了运维,目前万网已经支持域名解析加速,可以向com推送变更,1分钟内生效,各省的域名解析服务器目前应该有双向能力。
获取静态资源之前要建立通道,如果每次获取静态资源都要建立通道,会加大传输时间。
尽快渲染出,避免重排与重绘。
最大可以将图片减小到原图片的1/3大小,是google制定了,其它浏览器厂商并不吃google的菜,只有chrome支持,直接在网页保存webp格式的图片,本地不能预览,目前淘宝商品详情页采用这种格式图片。
不要在外链中使用document.write加载资源
doc 返回编码charset=uft-8,加快解析;不要在html中设置,貌似ie8下有bug。
将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
用jquery选择器选中的元素,如果还需第二次使用,建议缓存到变量中,避免重复的创建jQuery对象。
在查找元素时,使用对象比使用数组更有效,使用数组还需要遍历,但对象不会保持默认的排序。
dom操作是有代价的,访问它
有些性能优化点,需要前端工程师在编码过程中遵守;有些需要配置服务器,设置缓存、etag、gzip;ajax缓存需要后台提供接口时设置;一些关键的优化点,需要前端工程支持。
在模块化开发时,细分了许多小文件,如何合并压缩脚本、样式,并在多页面中利用好缓存,如何使css spirites、inline images用起来很easy。如何保证将样式表放在页面上方、将脚本放在底部、剔除重复的脚本。
expires 浏览器本地缓存设置
语法:expires [time|epoch|max|off]默认值:expires off使用字段:http, server, locationoff 将禁止修改头部中的 Expires和Cache-Control字段。Time控制“Cache-Control”的值,负数表示no-cacheepoch 将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;
性能
新闻热点
疑难解答