`

深入懒加载

阅读更多

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

懒加载(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 plaincopy
  1. return {  
  2.     init : _init,  
  3.     addCallBack : _addCallBack  
  4. };  

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

 

[javascript] view plaincopy
  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 plaincopy
  1. //自动模式  
  2. datalazyload.init({  
  3.     'mod' :auto  
  4. });  
  5.   
  6. //手动模式  
  7. datalazyload.init({  
  8.     'mod' :manual,  
  9.    'IMG_SRC_DATA'  : 'data-src'  
  10. });  

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

 

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

相关推荐

    hibernate 延迟加载深入剖析

    深入理解hibernate懒加载技术,正确使用懒加载

    Angular懒加载机制刷新后无法回退的快速解决方法

    估计是使用懒加载机制销毁了angular内部的state关联,导致无法回到上一个state(单击回退按钮 ui-routre的 $stateChangeStart 事件都不会触发),当然这只是猜测,由于事件关系也没有去深入的探究源码. angular懒加载...

    深入研究jQuery图片懒加载 lazyload.js使用方法

    主要介绍了jQuery图片懒加载 lazyload.js使用方法,通过设置临界点,占位符,设置事件来触发加载等等来讲解lazyload.js的使用,具体操作步骤大家可查看下文的详细讲解,感兴趣的小伙伴们可以参考一下。

    深入解析动态加载css的实现方法

    因为公司项目需要用到懒加载来提高网站加载速度,所以将非首屏渲染必需的css文件进行动态加载操作。 二、优化后的完整代码 /* * @function 动态加载css文件 * @param {string} options.url -- css资源路径 * @param ...

    动态加载css方法实现和深入解析

    因为公司项目需要用到懒加载来提高网站加载速度,所以将非首屏渲染必需的css文件进行动态加载操作。 二、优化后的完整代码 /* * @function 动态加载css文件 * @param {string} options.url -- css资源路径 * @...

    angularJS+requireJS实现controller及directive的按需加载示例

    最近因为项目的比较大,需要加载的js文件较多,为了提高首屏页面的加载速度,需要对js文件进行按需加载,然后网上参考了一些资料,自己也深入研究一番之后,实现了按需加载控制器js文件及指令js文件的效果;...

    浅谈vue加载优化策略

    首屏加载慢的原因无非就是单页面应用需要加载完整个路由表上的页面,而路由懒加载就是来解决这个问题的。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效...

    深入理解Java中的AQS.docx

    /*等待队列的队首结点(懒加载,这里体现为竞争失败的情况下,加入同步队列的线程执行到enq方法的时候会创 建一个Head结点)。该结点只能被setHead方法修改。并且结点的waitStatus不能为CANCELLED*/ private transient...

    MyBatis深入

    该讲义介绍了MyBatis从入门、核心配置文件详解、mapper映射文件详解、动态SQL、关系映射到懒加载等一系列知识点。

    前端开发工程师专用简历模板

    作为前端开发工程师,您的简历应该突出您的技能和经验,以吸引潜在的...优化页面加载速度,将图片压缩并使用懒加载技术,有效提升用户体验。 与后端团队密切合作,设计API接口以及与后端的数据交互。 等等 详细看简历

    blog:想更多!了解更多!

    personal blog 合抱之木,生于毫末;...Intersection Observer + Vue指令 优雅实现图片懒加载 【性能优化】图片优化——总览 事件循环机制 Event-Loop及其延伸 深入了解HTTP/2的前世今生以及Web性能优化总结

    深入理解Vue router的部分高级用法

    今天要介绍的是路由元信息,滚动行为以及路由懒加载这几个的使用方法。 1.路由元信息 什么是路由元信息,看看官网的解释,定义路由的时候可以配置 meta 字段可以匹配meta字段,那么我们该如何使用它,一个简单的例子...

    Hibernate学习总结

    学习成长路,Hibernate总结: 1.Hibernate入门优缺点、 2.Hibernate的操作CRUD、 3.主键生成机制、 ...7.懒加载、 8.Hibernate检索策略(fetch抓取策略)、 9.二级缓存、 10.Hbernate的检索方式(HQL语句)

    基于webpack4搭建的react项目框架的方法

    使用bundle-loader进行代码切割懒加载 手动搭建,不使用cli,大量注释适合初学者对webpack的理解学习,对react项目的深入了解 启动 git clone https://gitee.com/wjj0720/react-demo.git cd react-demo yarn ...

    knowledge-system:我的前端知识体系

    前端学习资料浏览器浏览器渲染渲染过程HTTP浏览器引擎HTMLCSSJavaScriptJS基础系列-冴羽JS基础系列-木易杨原生JS灵魂之问(下)JavaScript设计模式系列JavaScript常用八种继承方案图片懒加载JS相关问题javascript-...

    JAVA高并发高性能高可用高扩展架构视频教程

    WEB高级前后台分离思维-懒加载无限级树形菜单 动态页面的静态化处理 大并发展示优化,动态页面的静态化 深入理解JDK动态代理本质 企业级高并发缓存解决方案 性能优化之Oracle语句优化雾区 前后台数据验证架构源码级...

    yy_aosp:Android原始码适合学习

    读Android源码之前要准备读一些新的入门书籍:语言:《深度探索C ++对象模型》,对应的英文版是《 Inside C +++ Object Model》程序编译,链接,加载:《链接器和加载器》,对应的英文版是《 Linker and Loader》...

Global site tag (gtag.js) - Google Analytics