首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > Web前端 >

Lazyload 延迟加载成效

2012-11-01 
Lazyload 延迟加载效果Lazyload是通过延迟加载来实现按需加载,达到节省资源,加快浏览速度的目的。网上也有

Lazyload 延迟加载效果

Lazyload是通过延迟加载来实现按需加载,达到节省资源,加快浏览速度的目的。
网上也有不少类似的效果,这个Lazyload主要特点是:
支持使用window(窗口)或元素作为容器对象;
对静态(位置大小不变)元素做了大量的优化;
支持垂直、水平或同时两个方向的延迟。
由于内容比较多,下一篇再介绍图片延迟加载效果。
兼容:ie6/7/8, firefox 3.5.5, opera 10.10, safari 4.0.4, chrome 3.0

?

程序说明

【基本原理】

首先要有一个容器对象,容器里面是_elems加载元素集合。
用隐藏或替换等方法,停止元素加载内容。
然后历遍集合元素,当元素在加载范围内,再进行加载。
加载范围一般是容器的视框范围,即浏览者的视觉范围内。
当容器滚动或大小改变时,再重新历遍元素判断。
如此重复,直到所有元素都加载后就完成。


【容器对象】

程序一开始先用_initContainer程序初始化容器对象。
先判断是用window(窗口)还是一般元素作为容器对象:

ps:更多相关信息可以看“Finding the size of the browser window”。

在_load程序中,先根据位置参数、滚动值和阈值计算_range加载范围参数:

?包括以下几种模式:
vertical:垂直方向加载模式
horizontal:水平方向加载模式
cross/cross-vertical:垂直正交方向加载模式
cross-horizontal:水平正交方向加载模式
dynamic:动态加载模式
其中"dynamic"模式是一般的加载方式,没有约束条件,但也没有任何优化。
其余都属于静态加载模式,适用于加载对象集合元素的位置(相对容器)或大小不会改变(包括加载后)的情况。
其中两个正交方向加载模式("cross"模式)适用于两个方向都需要判断的情况。
程序会对静态加载的情况尽可能做优化,所以应该优先选择静态加载模式。


【动态加载】

动态加载是使用_loadDynamic程序作为加载程序的:

?在动态加载中,不会为元素记录位置参数,所以每次都会用_getRect程序获取加载元素的位置信息。
动态加载会默认使用"cross"模式来判断,即水平和垂直方向都判断。
如果元素在加载范围内,会执行_onLoadData自定义加载程序,进行元素的加载。


【静态加载】

静态加载是程序的重点,也是程序的主要特色。
主要是利用集合元素位置大小固定的性质进行优化,利用这个方式会大大提高程序执行效率,越多加载项会越明显。

原理是对加载集合进行排序,转换成有序集合,这样加载范围内的元素总是加载集合中连续的一段。
即可以把加载集合分成3部分,在加载范围前面的,在加载范围内的和加载范围后面的。
以horizontal模式左右滚动为例,加载过程大致如下:
1,记录每个元素的位置参数,按left坐标的大小对加载集合进行排序(从小到大),设置强制加载,跳到1.1;
1.1,记录加载范围,如果是强制加载,跳到1.2,否则跳到2;
1.2,设置索引为0,跳到3;
2,判断滚动的方向,如果向右滚动跳到3,否则跳到4,没有滚动的话取消执行;
3,向后历遍元素,判断元素是否在加载范围内,是的话跳到3.1,否则跳到3.2,如果没有元素,跳到6;
3.1,加载当前元素,并把它从集合中移除,跳回3;
3.2,判断元素的left是否大于容器的right,是的话跳到5,否则跳回3;
4,向前历遍元素,判断元素是否在加载范围内,是的话跳到4.1,否则跳到4.2,如果没有元素,跳到6;
4.1,加载当前元素,并把它从集合中移除,跳回4;
4.2,判断元素的right是否大于容器的left,是的话跳到5,否则跳回4;
5,当前元素已经超过了加载范围,不用继续历遍,跳到6;
6,合并未加载的元素,并记录当前索引,等待滚动,如果全部元素都加载了,就完成退出。
7,当容器滚动时,跳到1.1;当容器大小改变时,设置强制加载,跳到1.1;当容器位置发生变化时,需要重新修正元素坐标,跳到1;

首先加载元素会在_rect属性中记录位置参数,不用重复获取,是一个优化。
更关键的地方是每次滚动只需对上一次索引到加载范围内的元素进行判断,大大减少了判断次数。
大致理解了原理后,后面再详细分析。

在_initMode模式设置中,对静态加载的情况会调用_initStatic初始化静态加载程序。
并传递两个参数mode(模式)和direction(方向)。
根据方向判断方式分三种模式:"vertical"(垂直)、"horizontal"(水平)和"cross"(正交)。
这里先分析一下前两种模式。

在_initStatic程序中,先根据direction设置排序函数,再设置_setElems重置元素集合程序:

?其中_setElems有两个意义,一个是记录元素的坐标参数,还有是把加载集合用map转换成数组并排序。
因为自定义的加载集合有可以是NodeList,而用sort就必须先把它转换成数组。

最后设置_loadData加载函数:

?其中_loadStatic静态加载程序是程序的核心部分,优化的核心就所在。
这里给它包装了三个参数:
direction:方向获取程序的程序名;
beforeRange:判断是否超过加载范围前面的程序;
afterRange:判断是否超过加载范围后面的程序。
通过包装,除了方便参数的使用,还能使程序结构更加清晰。

direction可能是"_verticalDirection"(垂直滚动方向获取程序)或"_horizontalDirection"(水平滚动方向获取程序)。
在里面在调用_getDirection程序获取滚动方向:

?可以看出,这是包装了primary和secondary参数的_getCrossDirection程序。
其中primary是主滚动方向获取程序,secondary是次滚动方向获取程序。
在_getCrossDirection中会根据主辅方向的滚动情况设置正交滚动值:

在ie8两种情况都会触发onresize,但ie6/7只有第二种情况触发。
鉴于情况比较复杂,程序在使用document时才绑定事件,其他情况由使用者自己设置。

resize事件有不少的问题,处理时要小心。
chrome的resize有一个问题(bug?),每次触发resize都会执行两次事件,或者说会触发两次。
而ie就复杂了,window, body和documentElement的resize会相互影响。
在ie8测试以下代码:

?当上下拖放时,onresize只会触发一次,但左右拖放时会触发两次。
换成documentElement会有差不多的结果,两个一起用的话左右拖放时documentElement会触发两次,window一次。
只设置body的话感觉就正常了,上下左右都只会触发一次。
而documentElement和body同时设置的效果跟documentElement和window的效果差不多。
如果window和body同时设置的话,后一个会覆盖前一个。
看来window和body的onresize对应的是同一个对象事件,可能为了在body设置也能做到window一样的效果。
个人推测,window和documentElement多出的一次,可能是由于同时触发了body的resize造成的。
ps:onresize时,用srcElement获取不到触发元素,所以确定不了是那个元素触发的。
ie7的结果更ie8差不多,ie6就有些不同,不过估计也是盒模式的不同造成的。
具体产生原因还不清楚,这里我也很糊涂。虽然问题弄不清楚,解决方法还是有的。
要绑定resize就是因为视框范围发生了变化,要重新设置视框范围,那么可以通过看两次resize之间视框范围有没有变化来确实是否执行程序。
在resizeDelay方法中,就是通过clientWidth和clientHeight来判断的:

var oThis = this, delay = this.delay;if ( this._lock ) {    this._timer = setTimeout( function(){ oThis._delay(run); }, delay );} else {    this._lock = true; run();    setTimeout( function(){ oThis._lock = false; }, delay );}

?原理是用一个_lock属性,程序运行一次后_lock设为true,并用一个setTimeout延时设置它为false。
在锁定(_lock为true)期间,程序不会立即执行,达到延时的效果。
为了保证最后一次触发程序即使在锁定期间也能完成,还用了一个_timer来延时这次执行。

这种延时有什么好处,直接用setTimeout延时不是更简单方便吗?
首先直接用setTimeout只能保证最后一次程序能执行,而这种方式能保证第一次和最后一次都能执行。
直接用setTimeout更大的问题是,如果持续触发,会导致程序一直不能执行(前提是执行时有正确clear掉定时器),而这种方式能保证程序在时间段内执行一次。

还有一个方法是不绑定事件,只用setTimeout或setInterval来监听。
好处是没有连续触发的问题,也不会有resize的bug,但需要一直监听视框范围是否改变。
一般情况下,绑定事件效率应该比较好。


使用技巧

【选择模式】

如果加载元素位置固定大小不固定的情况下只能选择"dynamic"动态加载,否则应该优先选择静态加载。
在静态加载中,如果基本上是用于垂直或水平滚动,应该用"vertical"或"horizontal"模式。
两个方向都需要的话,如果主要是垂直滚动的话就用"cross-vertical"模式,否则用"cross-horizontal"模式。

【延迟html渲染】

Lazyload的一个作用就是延迟html渲染。
原理是先保存元素里面的html,当判断元素在加载范围里面时,再加载里面的html。
程序主要是做判断的部分,而如何保存和加载就看各位的想象力了。
以下几种方法个人认为还不错的:
1,ajax法:保存地址,加载时利用ajax读取实际内容并插入到元素中;
使用恰当的话能有效节省服务器资源,特别是要读数据库的地方,但响应速度受网络影响,而且不利seo,类似的还可以用iframe。
2,textarea法:把html保存到一个textarea中,加载时把value插入元素中;
利用了textarea的特性,第二个实例就使用了这个方法,淘宝用的也是这个方式,使用简单,响应速度快。
不过仅仅是html的话,貌似也没必要延迟,可以考虑关联一些dom操作之类的。
2,注释法:把html保存到一个注释中,加载时把内容插入元素中;
跟textarea法类似,但效率应该更好,加载时找出nodeType为8的节点,再把nodeValue插入元素中;
但在ie如果用innerHTML添加注释会被自动忽略掉,使用时注意。

以上方法都有一个问题,在不支持js的时候不能平稳退化,谁有更好的方法的话欢迎赐教。
除此之外,还可以用来延迟js执行,css渲染等,下一篇还会有图片的延迟加载。

【position的bug】

在写第一个实例的窗口模式时,遇到了两个bug:
在ie6/7,overflow为scroll或hidden的元素,其中position为absolute或relative的子孙元素会出现异常。
解决方法:
1.为包含块元素添加属性position:relative。
2.把该元素的position:relative属性去掉,使用默认的static定位,并通过margin-top等属性实现类似的效果。
参考自“IE6 CSS bug”。

还有一个问题是,在ie6,overflow为visible的元素,会被其内容撑开。
解决方法:
在ie6下,本来overflow为visible的元素设为hidden,并把内容position设为relative。
原理请看“IE6 overflow:visible bug”。

还要注意的是,加载元素只能是容器的子元素。


使用说明

实例化时,必须有一个元素集合作为参数,可以是元素数组或NodeList集合。

可选参数用来设置系统的默认属性,包括:
属性:??? 默认值//说明
container:?window,//容器
mode:??"dynamic",//模式
threshold:?0,//加载范围阈值
delay:??100,//延时时间
beforeLoad:?function(){},//加载前执行
onLoadData:?function(){}//显示加载数据

还提供了以下方法:
load:加载程序;
resize:容器大小改变加载程序,其参数说明是否重置元素集合;
delayLoad:延迟的load程序;
delayResize:延迟的resize程序;
isFinish:指明程序是否执行完成;
dispose:销毁程序,其参数说明是否加载所有元素。

?

热点排行