深入懒加载

[javascript] view plain copy

懒加载(LazyLoad)一直是前端的优化方案之一。它的核心思想是:当用户想看页面某个区域时,再加载该区域的数据。这在一定程度上减轻了服务器端的压力,也加快了页面的呈现速度。

懒加载多用于图片,因为它属于流量的大头。最典型的懒加载实现方案是先将需要懒加载的图片的src隐藏掉,这样图片就不会下载,然后在图片需要呈现给用户时再加上src属性。

公司内部库的懒加载正是采用这种方案。它会遍历页面中所有的图片,将其src缓存起来后删除图片的src属性,当图片进入用户的可视区域后再为图片附加src属性。这种方案存在着以下不足:

① 在IE和FF下,懒加载的脚本运行时,有部分图片已经于服务器建立链接,这部分abort掉,再在滚动时延迟加载,反而增加了链接数。

② 在chrome下,由于webkit内核bug,导致无法abort掉下载,懒加载脚本完全无用。

③ 它只能针对图片的懒加载,但无法懒加载页面的某个模块(即延迟渲染页面的DOM节点)。

因此,在原有的技术方案之上,必须实现新的方案来解决这些问题。受到淘宝的懒加载模块启发,思路如下:

① 提供一种方式来让我们手动为页面中每个需要懒加载的图片缓存它的src属性,例如:原来的图片为<img src="xxx.jpg" />,现在改为<img data-src="xxx.jpg">。这样,页面在解析的时候,所有懒加载的图片在所有的浏览器下都不会下载,图片进入视野区域时再将data-src赋值给src属性。

② 提供延迟加载页面模块的方案。将研究发现,textarea是个不错的容器,浏览器会将该标签内的内容当作普通文本看待。因此,可以将页面中需要懒加载的模块放入textarea容器中,带需要的时候再将其取出。淘宝美食网正是大量运用了模块延迟加载方案。http://chi.taobao.com/market/food/auto.php?spm=885.125570.154248.13.F5s7Bt

基于上述思路,我写了一个懒加载的组件。该组件基于jQuery,提供的接口如下:

[javascript] view plain copy

  1. return {
  2. init : _init,
  3. addCallBack : _addCallBack
  4. };

init函数可以初始化该组件,它提供给我们的自定义选项如下:

[javascript] view plain copy

  1. var config = {
  2. mod : ‘auto‘, //分为auto和manul
  3. IMG_SRC_DATA : ‘img-lazyload‘,
  4. AREA_DATA_CLS : ‘area-datalazyload‘
  5. };

mod 分为自动和手动模式,自动模式正是前面讨论到的目前存在的实现方案,而手动方式是后来讨论的方案①,在手动方式下,我们需要将每个需要懒加载的图片的src属性缓存到一个用户可以自定义的属性中,默认为‘img-lazyload‘,即原始的图片改为<img img-lazyload=‘xxx.jpg‘>。

此外,不管是自动模式和手动模式下,都可以进行模块的懒加载,这时候,需要在每个模块的外层添加textarea容器,并且,将其visibility属性设置为hidden,class设置为一个用户可以自定义的值,默认为‘area-datalazyload‘。

实例如下:

[javascript] view plain copy

  1. //自动模式
  2. datalazyload.init({
  3. ‘mod‘ :auto
  4. });
  5. //手动模式
  6. datalazyload.init({
  7. ‘mod‘ :manual,
  8. ‘IMG_SRC_DATA‘  : ‘data-src‘
  9. });

addCallback是特定元素即将出现时的回调函数。调用如下:

[html] view plain copy

  1. datalazyload.addCallback($el,function(event){
  2. //TO DO
  3. })

其中$el是某个需要延迟加载的jquery对象,function是自定义的回调函数。

组件适用场景:① 有许多图片的页面,例如游戏特权首页:http://vip.qq.com/game.html

② 有许多模块,并且每个模块分工明确的页面,例如淘宝美食:http://chi.taobao.com/market/food/auto.php?spm=885.125570.154248.13.F5s7Bt

组件如下:

[javascript] view plain copy

  1. /**
  2. * @fileOverview 数据懒加载组件
  3. * @require jQuery
  4. */
  5. datalazyload = (function($){
  6. var config = {
  7. mod : ‘auto‘, //分为auto和manul
  8. IMG_SRC_DATA : ‘img-lazyload‘,
  9. AREA_DATA_CLS : ‘area-datalazyload‘
  10. };
  11. var IMG_SRC_DATA = ‘‘;
  12. var AREA_DATA_CLS = ‘‘;
  13. //用来存放需要懒加载的图片和数据块
  14. var imgArr = [];
  15. var areaArr = [];
  16. //支持用户回调的事件类型
  17. var eventType = ‘lazy‘;
  18. /**
  19. * 提供给外部的接口
  20. * @param {Object} [userConfig] 用户自定义配置
  21. * @private
  22. */
  23. function _init(userConfig) {
  24. config = $.extend(config,userConfig);
  25. console.log(config);
  26. IMG_SRC_DATA = config.IMG_SRC_DATA;
  27. AREA_DATA_CLS = config.AREA_DATA_CLS;
  28. _filterItems();
  29. _initEvent();
  30. }
  31. /**
  32. * 处理需要懒加载的图片和数据块的入口
  33. * @private
  34. */
  35. function _filterItems() {
  36. _filterImgs();
  37. _filterAreas();
  38. }
  39. /**
  40. * 事件绑定
  41. * @private
  42. */
  43. function _initEvent() {
  44. $(window).scroll(_eventHandler);
  45. $(window).resize(_eventHandler);
  46. _eventHandler();
  47. }
  48. /**
  49. * 处理需要懒加载的图片
  50. * @private
  51. */
  52. function _filterImgs() {
  53. if (config.mod === ‘auto‘) {
  54. //自动模式
  55. var $imgs = $("img");
  56. $imgs.each(function() {
  57. imgArr.push(this);
  58. var $img = $(this);
  59. $img.targetY = _getTargetY($img[0]);//先计算出每个图片距离页面顶部的高度,避免在事件事件处理函数中进行大量重复计算
  60. var dataSrc = $img.attr(IMG_SRC_DATA);
  61. //对于已存在IMG_SRC_DATA的,可能其它实例处理过,我们直接跳过去
  62. if (!dataSrc) {
  63. $img.attr(IMG_SRC_DATA,$img.attr(‘src‘));
  64. $img.removeAttr(‘src‘);
  65. }
  66. });
  67. } else {
  68. //手动模式下,已经在需要懒加载的IMG中设置了IMG_SRC_DATA属性,所以不作任何处理
  69. var $imgs = $("img["+IMG_SRC_DATA+"]");
  70. $imgs.each(function() {
  71. imgArr.push(this);
  72. var $img = $(this);
  73. $img.targetY = _getTargetY($img[0]);//先计算出每个图片距离页面顶部的高度,避免在事件事件处理函数中进行大量重复计算
  74. });
  75. }
  76. }
  77. /**
  78. * 处理需要懒加载的数据块
  79. * @private
  80. */
  81. function _filterAreas() {
  82. var $areas = $("textarea[class=‘"+AREA_DATA_CLS+"‘]");
  83. $areas.each(function() {
  84. areaArr.push(this);
  85. var $area = $(this);
  86. $area.targetY = _getTargetY($area[0]);
  87. });
  88. }
  89. /**
  90. * window节点的scroll和resize的事件处理函数
  91. * @private
  92. */
  93. function _eventHandler() {
  94. $.each(imgArr,function(i,el){
  95. if (el !== undefined) {
  96. var $img = $(el);
  97. if (_checkBounding($img)) {
  98. $img.attr(‘src‘,$img.attr(IMG_SRC_DATA));
  99. $img.trigger(eventType);
  100. $img.unbind(eventType);
  101. delete imgArr[i];
  102. }
  103. }
  104. });
  105. $.each(areaArr,function(i,el){
  106. if (el !== undefined) {
  107. var $area = $(el);
  108. if (_checkBounding($area)) {
  109. $area.hide();
  110. $area.removeClass(AREA_DATA_CLS);
  111. var $div = $("<div></div>");
  112. $div.insertBefore($area);
  113. $div.html($area.val());
  114. delete areaArr[i];
  115. }
  116. }
  117. });
  118. }
  119. /**
  120. * 检查需要懒加载的节点是否进入可视区域
  121. * @param {jQuery Object} [el]
  122. * @private
  123. */
  124. function _checkBounding($el) {
  125. var scrollY = document.body.scrollTop || document.documentElement.scrollTop || window.pageYOffset || 0;//页面滚动条高度
  126. var seeY = window.innerHeight || document.documentElement.clientHeight;//浏览器可视区域高度
  127. if ($el.targetY) {
  128. var targetY = $el.targetY;
  129. } else {
  130. var targetY = _getTargetY($el[0]);
  131. }
  132. //当目标节点进入可使区域
  133. if (Math.abs(targetY - scrollY) < seeY) {
  134. return true;
  135. } else {
  136. return false;
  137. }
  138. }
  139. /**
  140. * 获取目标节点距离页面顶部高度
  141. * @param {HTML Element} [el]
  142. * @private
  143. */
  144. function _getTargetY(el) {
  145. var tp = el.offsetTop;
  146. if (el.offsetParent) {
  147. while (el = el.offsetParent) {
  148. tp += el.offsetTop;
  149. }
  150. }
  151. return tp;
  152. }
  153. /**
  154. * 特定元素即将出现时的回调函数
  155. * @param {jQuery Obj} [$el]
  156. * @param {Function} [func]
  157. * @private
  158. */
  159. function _addCallBack($el,func) {
  160. $el.bind(eventType,function(event) {
  161. func.call($el,event);
  162. });
  163. }
  164. return {
  165. init : _init,
  166. addCallBack : _addCallBack
  167. };
  168. })(jQuery);

转载来源:http://blog.csdn.net/huli870715/article/details/8126519

时间: 2024-10-05 14:52:07

深入懒加载的相关文章

关于Hibernate中立即加载和懒加载的区别

当1表与2表之间有OneToMany(类似)关系时候, 对于1表的实体类,设置FetchType=EAGER时(也就是立即加载),取1表数据,对应2表的数据都会跟着一起加载,优点不用进行二次查询. 缺点是严重影响数据查询时间,查询速度慢. FetchType=LAZY(也就是懒加载),此时查询时间大大缩短,缺点是查询表1的数据时,查询不到2表的数据. 不会主动取查询2表的数据.

页面图片懒加载

一.页面滚动加载图片的理解 当一个网页含有大量的图片的时候,往往在打开页面的加载的很慢,如现在的大型电商.新闻.微博等,如果一下加载整个网页的所有图片,那会是一个缓慢的过程,要想快速的浏览网页,我们这时候就需要用到了在滚动或滑动页面的时候加载图片的方法.即图片懒加载!!! 二.实现懒加载的思路 当打开页面的时候,刚开始不给显示图片的位置放图片,等滚动或滑动到哪里的时候在获取图片.当然如果我们不给图片src中放图片,就会出现先找不到图片的提示,这样肯定是不行的. 所以我们在放图片的位置刚开始让一张

懒加载 lazy loading

懒加载:延迟加载,当程序启动时不加载资源,当程序需要这些资源时再去加载.需要的时候再加载的一种方式,能够减少内存的占用,效率高.其本质是重写get方法. 背景:由于ios内存有限,如果我们一次性将所有资源加载完毕将极大的消耗内存,降低程序运行效率.所以就要将一些比较大的资源,如音频,数据,图片等大资源进行懒加载,就需要                                             先判断是否存在,如果不存在再创建实例化. 1 @property (nonatomic,

懒加载(延迟加载)之后,在使用数据过程中容易出现的bug

在UI中,使用懒加载,也就是延迟加载来加载数据的时候,总是会面临几个问题? 如:1. >为什么先创建NSArray属性? 2. >为什么重写NSArray的get方法? 3.>为什么要判断是否为空? 4.>为什么下方代码"//1"这里不用NSString stringWithFormat: 而"//2"这里要使用? 5.>同时"//2"这里为什么使用的是%ld 来作为占位符? 这些问题不搞懂,懒加载就很难通透,  代

懒加载的坑

先说如下东东: 比如我们申明这个鬼 @property (nonatomic, strong) NSArray *mineModelArray; 那么 _mineModelArray self.mineModelArray 这两个获取数组的原理是不一样的,(好多人都没注意这个) 第一个是就简单粗暴直接获取这个数组 第二个是调用@property的get方法获取到的,因为这个get方法返回的就是当前数组的的类型即一个数组.大多数情况下两者一样的的. 但是:如果在懒加载里面就不要再用self.min

Angular-ui-router + oclazyload + requirejs实现资源随route懒加载

本文主要说明在用AngularJS开发web应用中如何实现lazyload,使用AngularJS需要在前端实现路由功能,本文就介绍利用此点实现前端资源的懒加载. 问题 目前大部分AngularJS的应用用requirJS组织模块,但是很多都没有使用lazyload功能,在app.js中启动时将全部依赖加载进来,在模块功能较少,前端资源少的情况下没问题.那么问题来了,依赖资源过多时怎么办? build时利用grunt-contrib-requirejs提取合并文件,减少http请求,但是存在问题

懒加载(延迟加载)

懒加载FatchType.LAZY也称为延迟加载,是Hibernate3关联关系对象默认的加载方式,所谓懒加载就是当在真正需要数据的时候,才真正执行数据加载操作.简单理解为,只有在使用的时候,才会发出sql语句进行查询.懒加载的有效期是在session打开的情况下,当session关闭后,会报异常.当调用load方法加载对象时,返回代理对象,等到真正用到对象的内容时才发出sql语句. 急加载FatchType.EAGER 也成为立即加载,时立即执行sql语句.在session没有关闭的之前,如果

hibernate中懒加载急加载的区别,get方法和load方的区别法

懒加载是hibernate中的关联关系对象的默认方式,懒加载也会先去查询对象然后获取对象的id, 当正真要对数据进行使用时才会正真写sql语句. 懒加载的有效加载期是在session打开的时候,所以在我们要使用到懒加载的时候必须要保持session的开启. 急加载就是直接用sql语句去访问数据库,但速度相对于懒加载更快. get()方法在执行时会立刻向数据库发出sql语句. load()方法和懒加载类似也是只有当真正使用该实体的属性时才会发出sql语句: 还有就是当数据库中查询不到东西时,方法会

vue 组件按需引用,vue-router懒加载,vue打包优化,加载动画

当打包构建应用时,Javascript 包会变得非常大,影响页面加载.如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了. 结合 Vue 的 异步组件 和 Webpack 的 code splitting feature, 轻松实现路由组件的懒加载. 我们要做的就是把路由对应的组件定义成异步组件 const Foo = resolve => { // require.ensure 是 Webpack 的特殊语法,用来设置 code-split