Lazyload实践

1,817 views

Published on

lazyload

Published in: Technology, Education
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,817
On SlideShare
0
From Embeds
0
Number of Embeds
20
Actions
Shares
0
Downloads
37
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Lazyload实践

  1. 1. Lazyload实践<br />口碑网<br />吴天豪<br />
  2. 2. Introduce <br />吴天豪<br />口碑网-UED-前端开发工程师<br />大三<br />爱折腾<br />博客: www.f2es.com<br />
  3. 3. 关于交流会<br />主动分享<br />分享没有大小<br />一个困扰你很久的问题<br />一个五分钟的小发现<br />一个好的提高开发效率的工具<br />一份好的开发经验<br />
  4. 4. Lazyload<br />直译:惰性加载<br />一般我们都会叫它做“延迟加载”<br />http://en.wikipedia.org/wiki/Lazy_loading<br />
  5. 5. http://docs.kissyui.com/kissy/docs/datalazyload/index.html<br />http://www.appelsiini.net/projects/lazyload<br />http://developer.yahoo.com/yui/imageloader/<br />
  6. 6. Why lazyload<br />Start sooner<br />Stop waiting<br />
  7. 7. 一般我们会用于<br />加载js<br />加载html块<br />
  8. 8. 延迟加载js<br />http://book.douban.com/subject/5362856/<br />http://book.douban.com/subject/4719162/<br />
  9. 9. Total_Speed = Time_to_Download + Time_to_parse + Time_to_execute(evaluation)<br />
  10. 10. Js-其实是一个老话题了<br />为什么要延迟加载js<br />当浏览器下载执行js时,页面的下载和渲染处于等待状态<br />为什么停滞?<br />大家思考一下<br />1.当页面一边渲染html和css,一边下载并执行脚本,what will happen?<br />脚本操作的元素可能根本就还没有渲染出来<br />
  11. 11. 如果我们的脚本放在页面的顶部,比如head里面,或者把脚本在页面中间,页面会等待脚本下载和执行<br />这在页面生存周期中是必要的,因为脚本执行过程中可能会修改页面内容<br />
  12. 12. 组织脚本<br />脚本放到页面底部<br />合并URL (minify)<br />因为http请求会带来额外的性能消耗,因此下载单个100k的文件比下载4个25k的文件要快。<br />无阻塞的脚本<br />异步加载js文件<br />
  13. 13. 动态加载脚本<br />方式:<br />XHR<br />动态脚本<br />Defer<br />Async<br />…….<br />
  14. 14. 文件之间的依赖关系<br />执行顺序<br />并行下载<br />
  15. 15. 介绍一个比较少见的方式<br />Script in iframe<br />页面中的iframe和其他元素是并行加载的,这样就可以无阻塞的加载script脚本<br />存在跨域问题<br />
  16. 16. 动态脚本能很好的处理脚本之间的依赖关系<br />
  17. 17. 监听js是否加载并准备就绪<br />Firefox,Opera,Chrome,Safari 3+<br />script.onload<br />ie<br />script.readystatechange<br />script.readyState === “loaded” <br />script.readyState === “complete”<br />
  18. 18. LoadScript(“jQuery.js”, function(){<br /> $(function(){<br /> //jQuery code<br /> }; <br />}<br />
  19. 19. 不过…文件一多…<br />loadScript(“a.js”, function(){<br />loadScript(“b.js”, function(){<br />loadScript(“c.js”, function(){<br />loadScript(“d.js”, function(){<br /> alert(“哎呀我擦,终于加载好了”);<br /> }<br /> }<br /> }<br />}<br />
  20. 20. 这样看起来就舒服多了<br />Load([“a.js”, “b.js”, “c.js”, “d.js”] , function(){<br /> //do something <br />});<br />
  21. 21. 这样就更好了<br />Load(“d.js”, function(){<br /> //do something<br />})<br />“d.js” 依赖于[“a.js”, “b.js”, “c.js”]<br />
  22. 22. 豆瓣do<br />http://site.douban.com/widget/notes/22456/note/87598595/<br />
  23. 23. n.onload = n.onreadystatechange = function () {<br /> if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {<br /> _loaded[this.getAttribute('src')] = true;<br /> if (cb) {<br />cb(this.getAttribute('src'), context);<br /> }<br /> //防止ie6的内存泄漏问题<br />n.onload = n.onreadystatechange = null;<br /> }<br />};<br />
  24. 24. 加载单个文件的处理。接受五个参数:url, type(文件类型), charset(编码), cb(要对应执行的方法), context(执行上下文环境)<br />
  25. 25. Do.add('common', {path: 'http://img3.douban.com/js/site/packed_common0.js', type: 'js'});<br />Do.add('common-eventhandler', {path: 'http://img3.douban.com/js/site/packed_common_eventhandler0.js', type: 'js', requires: ['common']});<br />Do('common', 'common-eventhandler');<br />
  26. 26. 问题依然存在:<br />下载a.js -> 执行a.js回调 -> 下载b.js -> 执行b.js的回调-> 下载c.js -> 执行c.js的回调-> 下载d.js -> 执行d.js的回调<br />不能做到并行下载….<br />
  27. 27. 在chrome和safari里,通过动态script加载的脚本并不会按照插入的顺序执行,而是哪个先加载好哪个执行<br />That’s pretty bad!<br />
  28. 28. 那么在chrome/safari里,可以插入一个<script type=“text/cache” src=“#”>来实现,type=“text/cache”的脚本下载好后会缓存并触发onload,但不会执行。之后再插入script type=“text/javascript”,因为这个时候脚本都已经下载好,所以插入的顺序就是执行的顺序了<br />
  29. 29.
  30. 30. 多一次插入就多浪费一次资源<br />使用这个方法实属无奈<br />
  31. 31.
  32. 32. 最后,统一的解决方案<br />同域文件<br />用xmlhttprequest请求过来<br />这时,脚本是以字符串的形式传递过来的<br />var s = document.createElement('script'); <br />s.text = xhrObj.responseText;<br />head.appendChild(s);<br />
  33. 33. 这种方法避开了evil eval,并且和DOM Script一样,可以按顺序加载js文件,当然,不会阻塞后续资源的加载。更为重要的是,在所有浏览器下,这种方法都不会阻止渲染和onload事件,体现了XMLHTTPRequest异步请求的本色。<br />
  34. 34. 跨域的脚本<br />Firefox/opera里可以用动态 Script DOM element ,完美地实现并行下载和顺序执行<br />对于 Safari/Chrome/IE ,无法保证顺序执行,通过插入一个 <script type=“text/cache” src=“#”> 来实现。<br />
  35. 35. defer和async的区别<br />Defer 延迟<br />Async异步<br />
  36. 36. 关于defer<br /><script src=" " defer></script><br />加上 defer 等于在页面完全在入后再执行脚本,相当于 window.onload,但应用上比 window.onload更灵活<br />告诉浏览器这段脚本不需要立即执行,从而做到异步加载,不阻塞其他资源下载<br />在window.onload处理之前执行defer的脚本<br />
  37. 37. Defer的水很深…<br />http://msdn.microsoft.com/en-us/library/ms533719(VS.85).aspx<br />https://developer.mozilla.org/En/HTML/Element/Script#attr-defer<br />都没有涉及执行顺序的问题…<br />
  38. 38. defer<br />在ie下能保持执行顺序,无论哪个先下载好---《高性能网站建设进阶指南》35页<br />测试结果很怪异…<br />http://www.websiteoptimization.com/speed/tweak/defer/<br />动态插入的脚本,即使设置defer,依然不能保证执行顺序<br />
  39. 39. <head><br /><script defer> <br /> alert(3);<br /></script><br /><script><br /> alert(1);<br /></script><br /></head><br /><body><br /><script defer> <br /> alert(4);<br /></script><br /><script><br /> alert(2);<br /></script><br /><script><br />window.onload = function(){<br /> alert(5);<br /> }<br /></script><br /></body><br />http://stevesouders.com/cuzillion/?ex=10017&title=IE+Ensure+Order+Excution<br />
  40. 40. Html5 async<br />This Boolean attribute is set to indicate that the browser should, if possible, execute the script asynchronously. If the script cannot be executed asynchronously, or the browser doesn’t support this attribute, the script is executed synchronously (and loading the content blocks until the script finishes running).<br />加了这个属性就意味这该脚本的执行不会影响页面的加载<br />
  41. 41. 区别<br />
  42. 42. 回到根源<br />Reduce(减少文件大小)<br />Defer(延迟)<br />Go Async(异步)<br />Be Lazy(懒加载)<br />Bite Size(真的需要 JS 吗)<br />
  43. 43. 更好的了解script标签<br />https://developer.mozilla.org/En/HTML/Element/Script<br />http://msdn.microsoft.com/en-us/library/ms535892(VS.85).aspx<br />
  44. 44. Html lazyload<br />对html片段进行延迟加载<br />极大地减少页面渲染时间<br />同时解决了图片加载的问题<br />
  45. 45. 实践<br />Sina微博<br />Twitter<br />淘宝商城<br />等等<br />
  46. 46. 思路<br />1.正常渲染一屏多一点大小的页面<br />2.把剩下的html代码放到一个容器中,这时不会解析,以文本的形式存在<br />3.随着用户进行的页面滚动操作逐步渲染页面<br />4.为开发人员提供相应操作的接口<br />
  47. 47. 1.容器的选择<br />textarea<br />noscript<br />你会选择哪个?<br />
  48. 48. 容器要求<br />内部的元素全部以文本或者文本节点的形式解析<br />可以可判断位置<br />访问内部的文本<br />
  49. 49. Textarea<br />应用:淘宝商城<br />其实,挺好,貌似没有什么大问题,我们需要的基本功能都已经实现了<br />
  50. 50. noscript<br />在开启脚本的情况下,元素本身可以获取,但是不含占位属性,比如offsetTop,offsetLeft,或者height,width之类的<br />在IE789下,内部的文本节点是不可访问的或者说在浏览器在开启脚本的情况下渲染好之后就把内部的文本删除了,No dom, no data<br />
  51. 51. 看起来textarea对noscript有着压倒性的优势<br />
  52. 52. 是么?<br />
  53. 53. 你希望那些不开启脚本的用户就没法使用你的产品了么?<br />
  54. 54. 在口碑网上一次调查时,不开启脚本的访问用户占0.5%到1%,不是一个小数目<br />
  55. 55. 页面的优化和可用性不应该是两个不同的走向<br />
  56. 56. noscript解决方案<br />对于noscript的占位属性丢失<br />获取到noscript元素,并在外层套一个textarea元素,textarea元素用visibility:hidden隐藏<br />为什么不用display:none?<br />之后,判断noscript元素是否在视口内的任务就顺利移交给了textarea<br />为什么不用别的元素?比如div之类的<br />这里就涉及到了文本转义的问题<br />
  57. 57. noscript解决方案<br />对于IE789对noscript内部元素的处理<br />感谢stackoverflow,有位老兄也在研究这个问题<br />使用iframe来引入noscript元素,然后用ajax动态请求iframe的html代码,因为这部分代码实际上已经在用户浏览的页面上了<br />拿到完整的html之后用正则把每个noscript内部的文本切割到一个数组里<br />
  58. 58. <iframe class="k2-display-none" src="?noscript=true"></iframe><br />我们可以这样发一个请求<br />Y.io("?noscript=true", cfg);<br />o.responseText就是含有当前页面noscript标签的html字符串<br />
  59. 59. 坚持noscript<br />为了提高页面的可访问性<br />
  60. 60. 还有问题<br />开发人员的脚本和一些元素有依赖关系<br />如果元素没有加载就执行脚本会导致脚本报错<br />让开发人员在每次获取元素之前都先判断元素是否存在不靠谱,并且脚本并不能直接知道元素到底加载好了米<br />
  61. 61. 把脚本的初始化放到对应的html块里?<br />好处,脚本会随着元素的加载执行<br />好吧,这让页面的维护变的非常困难,脚本东一段西一段的<br />
  62. 62. 自定义事件<br />我们创建一个自定义事件,这个事件的目的就是在某个html块加载好的时候发出一个广播,让脚本知道所要操作的元素已经加载好。<br />脚本绑定这个事件。<br />
  63. 63. 自定义事件<br />Y. publish(evt_type, opts)<br />在Y.EventTarget. _yuievt.events里存储<br />Y. fire( evt_type )<br />触发绑定的回调事件队列<br />
  64. 64. 再具体一点<br />添加自定义事件<br />Y.pulish("lazyloadReady:" + i, {<br /> type : "lazyloadReady:" + i,<br /> preventable : false<br />});<br />
  65. 65. 触发事件<br />ev = "lazyloadReady:" + (i + 1);<br />Y.fire(ev);<br />
  66. 66. 绑定事件<br />Y.on("lazyloadReady:1" , function(){<br /> alert("第一个模块加载完成");<br />});<br />暂时使用了页面上noscript标签的序号作为事件名…还在考虑更好的方法<br />
  67. 67. 创建iframe元素带来的开销是其他元素的1~2数量级<br />频繁的字符串操作,性能上有待提高…<br />
  68. 68. 看看kissy怎么做的<br />dataLazyload.addCallback(el, fn) <br />表示当 el 即将出现时,触发 fn<br />具体实现我觉得应该是类似的,有兴趣的可以看看源代码的实现<br />
  69. 69. QA<br />
  70. 70. 谢谢<br />

×