让我们的页面跑得更快

2,780 views
2,652 views

Published on

网速培训相关资料

Published in: Technology
2 Comments
35 Likes
Statistics
Notes
No Downloads
Views
Total views
2,780
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
0
Comments
2
Likes
35
Embeds 0
No embeds

No notes for slide
  • 页面渲染过程
  • 让我们的页面跑得更快

    1. 1. PC 制作部网速组
    2. 2. 慢 500ms = 20% 将放弃访问 (Google) 慢 400ms = 5-9% 将放弃访问 (Yahoo!) 慢 100ms = 1% 将放弃交易 (Amazon) we 2.97 s they 1.67 s (2010.2.1--2010.3.1 ) 慢 1300 ms = ??% 将放弃访问 http://www.slideshare.net/stubbornella/after-yslow-a 我们?
    3. 3. 20/80 法则 0.16 s 后端 20% ( jsp 、 Apache 、 hibernate 、 Nginx....... )
    4. 4. 20/80 法则 ∞ s 前端 80% ( js 、 css 、 images 、 <script></script> 、 jQuery.... )
    5. 5. 我们的成果
    6. 6. Lady…
    7. 7. Let’s start!
    8. 8. 我们关注的指标 #1 首屏阻塞元素 首屏时间 总下载时间 下载速度 基础页下载字节数 DOM 元素个数 首屏对象数
    9. 9. 首屏阻塞元素 首屏时间 总下载时间 下载速度 基础页下载字节数 DOM 元素个数 首屏对象数 JS CSS 广告 解释、渲染
    10. 10. 首屏阻塞元素 首屏时间 总下载时间 下载速度 基础页下载字节数 DOM 元素个数 首屏对象数 用户第一感受
    11. 11. 首屏阻塞元素 首屏时间 总下载时间 下载速度 基础页下载字节数 DOM 元素个数 首屏对象数 未来关注的方向 页面复杂程度 真正的标准
    12. 12. 首屏阻塞元素 首屏时间 总下载时间 下载速度 基础页下载字节数 DOM 元素个数 首屏对象数 波动不可避免 南北有差异 用户有差异
    13. 13. 首屏阻塞元素 首屏时间 总下载时间 下载速度 基础页下载字节数 DOM 元素个数 首屏对象数 页面的原代码的体积 Pconline 375.6k/63.3k Pcauto 273.4k/49.6k Zol 349.8k/85.7k Autohome 320.0k/50.3k xxx 10.5k/4.4k
    14. 14. 首屏阻塞元素 首屏时间 总下载时间 下载速度 基础页下载字节数 DOM 元素个数 首屏对象数 document.getElementsByTagName(&quot;*&quot;).length Pconline 7951 Pcauto 4975 Pclady 3474 Zol 2867 Autohome 4649 瑞丽 2860 VS 内容为王 ? 页面复杂程度 Reflow
    15. 15. 首屏阻塞元素 首屏时间 总下载时间 下载速度 基础页下载字节数 DOM 元素个数 首屏对象数 决定首屏显示速度
    16. 16. 全面的监控体系 #2 基调网络 中国最大的第三网网络访问性能测试机构 监测点遍布全国各地 模拟用户真实感受 丰富的监控功能
    17. 17. 基调能做的也是我们关注的
    18. 18. 柱状图,看出与对手差距
    19. 19. 历史曲线图,优化前后的对比
    20. 20. 即时监测帮助了解个别页面的情况
    21. 21. 我们的工具 #3
    22. 22. Httpwatch 概览、直观、易用
    23. 23. Dynatrace 深入、透彻、全面
    24. 24. 浏览器会偷懒么? http://stevesouders.com/cuzillion/?c0=bi1hfff0_0_f&c1=bb0hfff0_2_f&c2=bi1hfff0_0_f&t=1271640890625 Dynatrace httpwatch
    25. 25. 计算普通的布局 计算浮动的布局 安排布局任务 求 css 的值 输出 Rendering
    26. 26. Fiddler 本地调试必备,监测映射两不误
    27. 27. Y-slow YUI 页面效率黄金准则 PS :整合的图片优化功能很好用
    28. 28. Page speed Google 的页面效率准则
    29. 29. 页面渲染过程 #4
    30. 30. 渲染 3 4 5 6 7 计算的样式 <style> body{...} .wrap{...} .xx{...} .header{...} .content{...} </style> 渲染块 渲染块 渲染块 <div class=“xx”></div> <style> .xx{......;background:url( logo.jpg )} </style> <style> .... </style> <html> <div class=“xx”></div> <script></script> .... .... <style> content{...} </style> <ul></ul> </html> 1 2 8
    31. 31. 三大优化 #5
    32. 32. A. 样式精简合并 新页面—— CSS 模块化:后期维护成本 操作习惯的改变 经验的总结归纳 旧页面—— CSS 提取: Dust-Me DOM 操作、动态页使得提取存在风险 ? 更好的工具 :更加细心
    33. 33. CSS 提取步骤: <ul><li>备份页面用到的所有 CSS ,假设: a.css 、 b.css </li></ul><ul><li>将 a.css 、 b.css 整合到一个新的 CSS : new.css </li></ul><ul><li>将页面和 new.css 放到 ftp ,并让页面引用 new.css </li></ul><ul><li>用 Dust-Me 提取用到的 css 行号 </li></ul><ul><li>连同行号和 new.css 的内容一起,放到 http://zzb.pcauto.com.cn/svn/extractCSS.htm 做处理 </li></ul><ul><li>保存新的精简样式表 speed.css </li></ul><ul><li>Fiddler 本地调试,上传 </li></ul>
    34. 34. 注意事项: <ul><li>提取行号时,需在 used selectors 选项下用 crtl+A ,包括链接也要一起选中,如右下图 </li></ul><ul><li>Dust-me 无法提取到 js 动态加入的元素的 css ( eg :弹出框),所以在提取前要先了解页面 js 功能的情况,在本地调试时手动加入 </li></ul><ul><li>动态页大多分为很多块,页面上有可能有些板块看不到( eg :论坛的版主才能看到的版块),所以需和负责该动态应用的技术提前沟通,尽量让技术提供具有全部板块的页面 </li></ul><ul><li>样式提取需保证 Dust-me 监测的 CSS 要和放到 extractCSS.htm 的 CSS 是同一个,否则会出错; Dust-me 每次使用前需清除掉之前的记录 </li></ul>
    35. 35. B.Js 延迟 defineJS () 外链 js 用到时才请求 抵制浪费 各取所需
    36. 36. defineJS () : 同步: XMLhttprequest 不能跨域 默认 UTF-8 ,如 js 含有中文需改为 UTF-8 格式 异步: getElementsByTagName(&quot;head&quot;)[0].appendChild(js); 可以跨域 异步对页面不会阻塞 如果有用到监听函数的返回值,则需要改为同步方式 eg : jQuery defineJS(“ !$,!jQuery,!$.getScript = /www1/js/pc.jquery1.4.js ”);// 同步 defineJS(“ featuredcontentslider.init =1000: http://www1.pconline.com.cn/global/2008/js/contentslider.js ”); // 异步
    37. 37. defineJS 使用步骤 : <ul><li>在页面头部加入 defineJS 函数定义,到 http://zzb.pcauto.com.cn/svn/tips/defineJS.js 复制压缩版 </li></ul><ul><li>记录页面用到的全部外链 js ,包括 js 地址、函数名、是否要即时调用返回值 </li></ul><ul><li>用以下方法定义所有的外链 js : defineJS(“featuredcontentslider.init=1000:http://www1.pconline.com.cn/global/2008/js/contentslider.js”); (注意异步和同步写法上有区别) </li></ul><ul><li>上传 ftp 测试各 js 功能是否正常、页面有无报错 </li></ul><ul><li>更新 </li></ul>
    38. 38. defineJS () : javascript:alert(defineJSlog) 记录每个请求 js 的函数
    39. 39. $.lazy() : jQuery 插件的 defineJS var p='http://www1.pconline.com.cn/js/'; $. lazy ( { name:‘cluetip’, //jQuery 插件名 cache:true, // 是否缓存 src:p+‘jquery.cluetip.js’,// 插件地址 dependencies:// 插件依赖的资源 { js:[p+'jquery.hoverIntent.js'], css:[p+'jquery.cluetip.css'] } }); $. lazy ({name:'bt', cache:true, src:p+'jquery.bt.js'}); http://www.pconline.com.cn/www1/js/pc.jquery1.4.js
    40. 40. 接口后出: var old_node=document.getElementById(&quot;old_node&quot;); var s=document.getElementById(&quot;new_node&quot;).getElementsByTagName(&quot;script&quot;); for(var i=0;i<s.length;i++){ s[0].parentNode.removeChild(s[0]);// 防止接口重复请求 } old_node .parentNode.replaceChild(document.getElementById(&quot; new_node &quot;), old_node ); <div id=&quot; new_node &quot;><script class=&quot; defer&quot; src=&quot;http://game.pcgames.com.cn/jsp/interface/item_piece_js.jsp?id=&quot;></script></div> <div id=&quot;topTxt&quot;></div> <div id=&quot;topTxt&quot;></div> <div id=&quot; old_node &quot;></div>
    41. 41. setTimeout : <div id=&quot;topTxt&quot;></div> <div id=&quot;topTxt&quot;></div> if(window.ajaxLogon0906) ajaxLogon0906(); else setTimeout (function(){ var js=document.createElement(&quot;script&quot;); js.src=&quot;http://www1.pconline.com.cn/global/2009/js/chalogon09.js&quot;; document.getElementsByTagName(&quot;head&quot;)[0].appendChild(js); },1000) // 登陆 js 后出 对于网速较慢的用户后出效果并不理想 比如汽车网首页品牌下拉框,当鼠标移上去时才初始化。一些不是一开始就需要的效果,可在 body.onload 里加载,为避免代码冲突,可以用下面的代码: if(window.attachEvent) document.body.attachEvent(&quot;onload&quot;,fn); else document.body.addEventListener(&quot;load&quot;,fn);
    42. 42. 普通调用事件化: <div id=&quot;topTxt&quot;></div> <div id=&quot;topTxt&quot;></div> if(window.attachEvent) document.body. attachEvent (&quot;onload&quot;,fn); else document.body. addEventListener (&quot;load&quot;,fn); 比如汽车网首页品牌下拉框,当鼠标移上去时才初始化。一些不是一开始就需要的效果,可在 body.onload 里加载,为避免代码冲突,可以用下面的代码: 比如汽车网首页品牌下拉框,当鼠标移上去时才初始化。一些不是一开始就需要的效果,可在 body.onload 里加载,为避免代码冲突,可以用下面的代码: if(window.attachEvent) document.body.attachEvent(&quot;onload&quot;,fn); else document.body.addEventListener(&quot;load&quot;,fn);
    43. 43. C. 广告后出
    44. 44. 广告处理前: 请求多,且造成页面阻塞
    45. 45. 广告处理后: 首屏没有广告请求和阻塞元素
    46. 46. <c:set var=&quot;combineAD&quot;> <body> ..... </body> </c:set> <cms: combineAD insertBefore ='<div class=&quot;screen&quot; id=&quot;divScreen6&quot;>'>${combineAD}</cms:combineAD> <ul><li>combineAD 会将所包含的内容中的广告请求合并为一个 </li></ul><ul><li>insertBefore 参数为插入广告请求的位置 </li></ul><ul><li>动态应用需在头部引入 combineAD.tag ,采编已加入 </li></ul>
    47. 47. 广告请求 后出位置
    48. 48. 更有效率的 JavaScript #6
    49. 49. js 是页面效率的噩梦!
    50. 50. var form=document.forms[0]; if(form.txtName.length==0){ alert(&quot; 用户名不能为空 &quot;); return false; } 以前
    51. 51. 现在 ...
    52. 52. 现在 ...
    53. 53. 页面效率的问题 80% 由 js 引起
    54. 54. <ul><li>Js 存在的问题 </li></ul><ul><li>js 的请求阻塞其后的请求, js 的执行阻塞页面的渲染( js 延迟) </li></ul><ul><li>ECMAScript </li></ul><ul><li>DOM 操作 </li></ul><ul><li>jQuery </li></ul>
    55. 55. ECMAScript var divs=document.getElementsByTagName(&quot;div&quot;); for(var i=0;i< divs.length;i++){ } 缓存节点集合的长度,效率更高( ie 下快 60 倍) var divs=document.getElementsByTagName(&quot;div&quot;); for(var i=0, len=divs.length ;i<len;i++){ } 提高循环效率
    56. 56. 减少对象查找 <ul><li>function getHtml(data) { </li></ul><ul><ul><li>var sTypeNum = data.sTypeItems.length; </li></ul></ul><ul><ul><li>var html = &quot;&quot;; </li></ul></ul><ul><ul><li>for (i = 0; i < sTypeNum; i++) { </li></ul></ul><ul><ul><ul><li>var d = data.sTypeItems[i]; </li></ul></ul></ul><ul><ul><ul><li>var bNum = d .brandItems.length; </li></ul></ul></ul><ul><ul><ul><li>var pNum = d .priceItems.length; </li></ul></ul></ul><ul><ul><ul><li>html += '<a href=&quot;' + data.sTypeItems[i] .sTypeDir + '&quot; class=&quot;act&quot; id=&quot;td_' + d .sTypeID </li></ul></ul></ul><ul><ul><ul><li>+ '&quot;>' + d .sTypeName + '</a>'; </li></ul></ul></ul><ul><ul><ul><li>html += '<div class=&quot;hid treeBody&quot; id=&quot;subTable' + d .sTypeID + '&quot;>'; </li></ul></ul></ul><ul><ul><ul><li>if (bNum > 0) { </li></ul></ul></ul><ul><ul><ul><ul><li>html += '<div class=&quot;Br1&quot;> 品牌 </div>'; </li></ul></ul></ul></ul><ul><ul><ul><ul><li>html += '<div class=&quot;Br2&quot;>'; </li></ul></ul></ul></ul><ul><ul><ul><ul><li>for (k = 0; k < bNum; k++) { </li></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>var brand = d.brandItems[k]; </li></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>html += '<a href=&quot;' + brand .brandUrl + '&quot;>' + brand .brandName </li></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>+ '</a>'; </li></ul></ul></ul></ul></ul><ul><ul><ul><ul><li>} </li></ul></ul></ul></ul><ul><ul><ul><ul><li>if (bNum >= 15) </li></ul></ul></ul></ul><ul><ul><ul><ul><li>html += '<a class=&quot;more&quot; href=&quot;' + d .moreUrl + '&quot;> 更多 </a>'; </li></ul></ul></ul></ul><ul><ul><ul><ul><li>html += '</div>'; </li></ul></ul></ul></ul><ul><ul><ul><li>} </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul>
    57. 57. 字符串合并 var reselt=“aaa”+”bbb”; 下面的代码效率更高,但不易维护,如果字符串相加操作量不大可不用以下方式 var buf=new Array(); buf.push(“aaa”); buf.push(“bbb”); var result=buf.join(“”); a += 'x' + 'y'; 后者比前者快 20% ,而且消耗更少的内存: a += 'x'; a += 'y';
    58. 58. var min = Math.min(a,b); A.push(v); 下面代码实现相同功能,但效率更高: var min = a < b ? a : b; A[A.length] = v; 还有 ... 基本运算符比函数调用更快
    59. 59. var original = document.getElementById('container'); var cloned = original. cloneNode (true); // 克隆节点,处理完后再替换原节点 cloned.setAttribute('width','50%'); var elem, contents; for( var i = 0; i < textlist.length; i++ ) {    elem = document.createElement('p');    contents = document.createTextNode(textlist[i]);    elem.appendChild(contents);    cloned.appendChild(elem); } original.parentNode. replaceChild (cloned,original); DOM 元素处理尽量脱离 DOM 树 if(window.ajaxLogon0906) ajaxLogon0906(); else setTimeout(function(){ var js=document.createElement(&quot;script&quot;); js.src=“http://www1.pconline.com.cn/global/2009/js/chalogon09.js”;// 先设置 src ,再插入 document.getElementsByTagName(&quot;head&quot;)[0].appendChild(js); },1000)
    60. 60. var toChange = document.getElementById('mainelement'); toChange.style.background = '#333'; toChange.style.color = '#fff'; toChange.style.border = '1px solid #00f'; 通过修改元素 class 名使用新样式,效率更高, reflow 更少: div {    background: #ddd;    color: #000;    border: 1px solid #000; } div.highlight {    background: #333;    color: #fff;    border: 1px solid #00f; } ... document.getElementById('mainelement').className = 'highlight'; 修改元素 class 名
    61. 61. document.getElementById('test').property1 = 'value1'; document.getElementById('test').property2 = 'value2'; document.getElementById('test').property3 = 'value3'; document.getElementById('test').property4 = 'value4'; 下面的代码比上面的代码要快 5-10 倍: var sample = document.getElementById('test'); sample .property1 = 'value1'; sample .property2 = 'value2'; sample .property3 = 'value3'; sample .property4 = 'value4'; 用变量保存 DOM 值
    62. 62. jQuery 选择符本质: $(“#id”) : document.getElementById(“id”); // 快 $(“div”) : document.getElementsByTagName(“div”); // 较慢 $(“.class”) : ????? // 很慢很慢 var all=document.getElementsByTagName(“ * ”); for(var i=0;i<all.length;i++){ if(all[i].className=“class”){ return all[i]; } } $(“.class”,$(“#id”)) // 好一些 jQuery 不要直接使用 class 作为选择符 http://blog.dynatrace.com/2009/11/09/101-on-jquery-selector-performance/ $(“#id”).addClass(“yyy”); $(“#id ul”).show(); $(“#id ul li”).addClass(“xxx”); // 慢 var div=$(“#id”); div.addClass(“yyy”); $(“ul”,div).show(); $(“li”,div).addClass(“xxx”);
    63. 63. $(‘#traffic_light input.on).bind(’click‘, function(){…}); $(’#traffic_light input.on).css(‘border’, ‘3px dashed yellow’); $(‘#traffic_light input.on).css(’background-color‘, ‘orange‘); $(’#traffic_light input.on).fadeIn(’slow’); 将 jquery 对象缓存起来 http://blog.dynatrace.com/2009/11/09/101-on-jquery-selector-performance/ var $active_light = $(‘#traffic_light input.on’); $active_light.bind(‘click’, function(){…}); $active_light.css(‘border’, ‘3px dashed yellow’); $active_light.css(‘background-color’, ‘orange’); $active_light.fadeIn(’slow’); OR $(‘#traffic_light input.on’).bind(‘click’, function(){…}).css(‘border’, ‘3px dashed yellow’).css(‘background-color’, ‘orange’).fadeIn(’slow’);
    64. 64. $(document).rady 在 DOM 载入时执行,有可能会延迟不够,影响到页面的渲染 $(window).load http://blog.dynatrace.com/2009/11/09/101-on-jquery-selector-performance/ $(window).load 在所有内容加载后触发,包括窗口,框架,对象和图像
    65. 65. 不在首屏内使用 jQuery http://blog.dynatrace.com/2009/11/09/101-on-jquery-selector-performance/ 对于 jQuery 的优化会用到 defineJS 的延迟去加载,如果在首屏使用 jQuery ,会使 jQuery 的请求处在首屏,失去了延迟的意义 所以: 将 jQuery 的调用放到首屏后 或者 NO jQuery
    66. 66. 还有什么? #7
    67. 67. Reflow 和 Repaint
    68. 68. 图片按需加载 http://price.pcauto.com.cn/serial.jsp?sid=4523 http://product.pconline.com.cn/mobile/
    69. 69. CSS sprite <ul><li>单张图片尽量小于 25k </li></ul><ul><li>在不影响维护的前提下,尽量减少图片的空白 </li></ul><ul><li>PNG </li></ul>
    70. 70. 减少图片大小重置 页面显示的大小 88x88 图片实际大小 232x232 <ul><li>浪费带宽传输 </li></ul><ul><li>影响页面渲染 </li></ul>http://www.chencheng.org/blog/2009/12/17/resized-image-checker/
    71. 71. CSS 选择符对页面的影响 1. 页面元素越多,渲染时间对页面速度的影响越明显。 2. 选择符从右至左匹配,最右边的选择符匹配效率最为关键。 http://stevesouders.com/efws/css-selectors/csscreate.php
    72. 72. 不要使用 CSS Expression <ul><li>Js 写在 css 里随时都会触发: mouse move, key press, resize, scroll, etc. </li></ul><ul><li>将 js 封装为函数可以只触发一次 </li></ul>http://stevesouders.com/hpws/rule-expr.php <ul><li>更好的方式是将 js 绑定到事件,保持干净的 CSS </li></ul>
    73. 73. 浏览器还是服务器? json vs innerHTML
    74. 74. <base target=&quot;_blank&quot; /> <ul><li>节省带宽? Pcgames 199k->186k </li></ul><ul><li>据说影响页面渲染? </li></ul>
    75. 75. iframe 其中样式全内嵌 iframe 中的样式表引用同样会引起父页面假死和花屏!
    76. 76. 并发 ie 中,同一个 script 中用 document.write 可以并发 // 并发 js 和 css <script> document.write(“a.css”); document.write(“b.js”); </script> // 并发 js 和 js <script> document.write(“a.js”); document.write(“b.js”); </script> 为保证模板在 DW 中的效果,样式表在 <head> 中写成 <c:set var=&quot;CSS&quot;> <link href=&quot;....&quot; rel=&quot;...&quot;/> </c:set> 然后在 body 中 ivy.js 的地方,写成 <script> document.write('${CSS}'); if(!window.addIvyID)document..... //ivy.js document.write(&quot;<script src= 头部 js></script>&quot;); </script>
    77. 77. 探索还未结束 #8
    78. 78. IE6 下背景图加载顺序测试 <div style=&quot;background:url(bg.png)&quot;></div><img src=&quot;img1.jpg&quot;/> 很多图 <div style=&quot;background:url(bg.png)&quot;><img src=&quot;img1.jpg&quot;/><img src=&quot;img2.jpg&quot;/></div> <div style=&quot;background:url(bg.png)&quot;></div><img src=&quot;img1.jpg&quot;/><script></script><img src=&quot;img2.jpg&quot;/> 加载顺序 : img1.jpg > 很多图 > bg.png 加载顺序 : img1.jpg > bg.png > img2.jpg 加载顺序 : img1.jpg > bg.png > img2.jpg 初探 IE6 的内部机制
    79. 79. 页面卡住不动了? |------------| | 图 | |------------| | 外链 Js | |------------| | 内容 | |____________|
    80. 80. 精简:舌签精简版 兼容:伪下拉框
    81. 81. The End

    ×