“按需加载”的应用
来源:互联网 发布:linux 下except用法 编辑:程序博客网 时间:2024/06/03 23:49
按需加载是前端性能优化中的一项重要措施,按需加载是如何定义的呢?顾名思义,指的是当用户触发了动作时才加载对应的功能。触发的动作,是要看具体的业务场景而言,包括但不限于以下几个情况:鼠标点击、输入文字、拉动滚动条,鼠标移动、窗口大小更改等。加载的文件,可以是JS、图片、CSS、HTML等。后面将会详细介绍“按需”的理解。
按需解析HTML
按需解析HTML,就是页面一开始不解析HTML,根据需要来解析HTML。解析HTML都是需要一定时间,特别是HTML中包含有img标签、引用了背景图片时,如果一开始就解析,那么势必会增加请求数。常见的有对话框、拉菜单、多标签的内容展示等,这些一开始是不需要解析,可以按需解析。实现按需解析,首先用<script type=”text/x-template”>html</sccript> 这个标签来对忽略对HTML的解析。然后根据触发的动作,把script里面的HTML获取出来,填充到对应的节点中。
示例代码如下:
01
<!DOCTYPE html>
02
<
html
>
03
<
head
>
04
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=utf-8"
/>
05
<
title
>按需解析HTML</
title
>
06
</
head
>
07
<
body
>
08
<
script
type
=
"text/x-template"
id
=
"suc_subscription"
>
09
<!--假设这里的样式box-dytz 引用了一张背景图--->
10
<
div
>
11
<!--这里暂且用这张图片作为测试,实际中,大家可以替换为任何图片-->
12
<
img
src
=
"http://tid.tenpay.com/wp-content/uploads/2012/12/按需加载.jpg"
/>
13
</
div
>
14
</
script
>
15
<
div
id
=
"success_dilog"
></
div
>
16
<
input
type
=
"button"
value
=
"点我展示HTML"
onclick
=
"showHTML()"
/>
17
<
script
>
18
function showHTML(){
19
document.getElementById('success_dilog').innerHTML = document.getElementById('suc_subscription').innerHTML;
20
}
21
</
script
>
22
</
body
>
23
</
html
>
我们一起来看下demo,当运行demo并抓包发现:当页面加载结束时,并没有看到图片的请求;当点“点我展示HTML”按钮时,通过抓包发现有图片请求。
曾经做个demo并经过测试发现,如果是直接解析HTML(不包含有请求CSS图片和img标签),耗费的时间要比用<script type=”text/x-template”>html</script>大约慢1-2倍,如果是还包括请求有CSS图片、img标签,请求连接数将会更多,可见按需解析HTML,对性能提升还是有一定效果。
按需加载图片
按需加载图片,就是让图片默认开始不加载,而且在接近可视区域范围时,再进行加载。也称之为懒惰加载。大家都知道,图片一下子全部都加载,请求的次数将会增加,势必影响性能。
先来看下懒惰加载的实现原理。它的触发动作是:当滚动条拉动到某个位置时,即将进入可视范围的图片需要加载。实现的过程分为下面几个步骤:
- 生成<img data-src=”url”>标签时,用data-src来保存图片地址;
- 记录的图片data-src都保存到数组里;
- 对滚动条进行事件绑定,假设绑定的函数为function lazyload(){};
- 在函数lazyload中,按照下面思路实现:计算图片的Y坐标,并计算可视区域的高度height,当Y小于等于(height+ scrollTop)时,图片的src的值用data-src的来替换,从而来实现图片的按需加载;
下面看一个示例代码:
001
<!DOCTYPE html>
002
<
html
>
003
<
head
>
004
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=utf-8"
/>
005
<
title
>图片按需加载</
title
>
006
</
head
>
007
<
body
>
008
<
style
>
009
li {float:left;width:200px;}
010
</
style
>
011
<
div
style
=
"widh:100%;height:1200px;border:1px solid #000"
>这里是空白的内容,高度为900像素,目的是方便出现滚动条</
div
>
012
<
ul
style
=
"width:600px;"
>
013
<
li
> <
img
width
=
"158"
height
=
"158"
data-src
=
"http://pic6.nipic.com/20100423/4537140_133035029164_2.jpg"
/> </
li
>
014
<
li
> <
img
width
=
"158"
height
=
"158"
data-src
=
"http://www.jiujiuba.com/xxpict2/picnews/62223245.jpg"
/> </
li
>
015
<
li
> <
img
width
=
"158"
height
=
"158"
data-src
=
"http://www.bz55.com/uploads/allimg/100729/095KS455-0.jpg"
/> </
li
>
016
<
li
> <
img
width
=
"158"
height
=
"158"
data-src
=
"http://www.hyrc.cn/upfile/3/200611/1123539053c7e.jpg"
/> </
li
>
017
<
li
> <
img
width
=
"158"
height
=
"158"
data-src
=
"http://www.mjjq.com/blog/photos/Image/mjjq-photos-903.jpg"
/> </
li
>
018
<
li
> <
img
width
=
"158"
height
=
"158"
data-src
=
"http://c.cncnimg.cn/000/954/1416_2_b.jpg"
/> </
li
>
019
<
li
> <
img
width
=
"158"
height
=
"158"
data-src
=
"http://www.jiujiuba.com/xxpict2/picnews/62223231.jpg"
/> </
li
>
020
<
li
> <
img
width
=
"158"
height
=
"158"
data-src
=
"http://www.mjjq.com/pic/20070530/20070530043314558.jpg"
/> </
li
>
021
</
ul
>
022
<
script
>
023
var API = {
024
/**
025
* 兼容Mozilla(attachEvent)和IE(addEventListener)的on事件
026
* @param {String} element DOM对象 例如:window,li等
027
* @param {String} type on事件类型,例如:onclick,onscroll等
028
* @param {Function} handler 回调事件
029
*/
030
on: function(element, type, handler) {
031
return element.addEventListener ? element.addEventListener(type, handler, false) : element.attachEvent('on' + type, handler)
032
},
033
/**
034
* 为对象绑定事件
035
* @param {Object} object 对象
036
* @param {Function} handler 回调事件
037
*/
038
bind: function(object, handler) {
039
return function() {
040
return handler.apply(object, arguments)
041
}
042
},
043
/**
044
* 元素在页面中X轴的位置
045
* @param {String} element DOM对象 例如:window,li等
046
*/
047
pageX: function(El) {
048
var left = 0;
049
do {
050
left += El.offsetLeft;
051
052
} while(El.offsetParent && (El = El.offsetParent).nodeName.toUpperCase() != 'BODY');
053
return left;
054
055
},
056
/**
057
* 元素在页面中Y轴的位置
058
* @param {String} element DOM对象 例如:window,li等
059
*/
060
pageY: function(El) {
061
var top = 0;
062
do {
063
top += El.offsetTop;
064
065
} while(El.offsetParent && (El = El.offsetParent).nodeName.toUpperCase() != 'BODY');
066
return top;
067
068
},
069
/**
070
* 判断图片是否已加载
071
* @param {String} element DOM对象 例如:window,li等
072
* @param {String} className 样式名称
073
* @return {Boolean} 布尔值
074
*/
075
hasClass: function(element, className) {
076
return new RegExp('(^|\\s)' + className + '(<
a
href
=
"$)').test(element.className"
>\\s|$)').test(element.className</
a
>)
077
},
078
/**
079
* 获取或者设置当前元素的属性值
080
* @param {String} element DOM对象 例如:window,li等
081
* @param {String} attr 属性
082
* @param {String} (value) 属性的值,此参数如果没有那么就是获取属性值,此参数如果存在那么就是设置属性值
083
*/
084
attr: function(element, attr, value) {
085
if (arguments.length == 2) {
086
return element.attributes[attr] ? element.attributes[attr].nodeValue : undefined
087
}
088
else if (arguments.length == 3) {
089
element.setAttribute(attr, value)
090
}
091
}
092
};
093
094
/**
095
* 按需加载
096
* @param {String} obj 图片区域元素ID
097
*/
098
function lazyload(obj) {
099
this.lazy = typeof obj === 'string' ? document.getElementById(obj) : document.getElementsByTagName('body')[0];
100
this.aImg = this.lazy.getElementsByTagName('img');
101
this.fnLoad = API.bind(this, this.load);
102
this.load();
103
API.on(window, 'scroll', this.fnLoad);
104
API.on(window, 'resize', this.fnLoad);
105
106
}
107
lazyload.prototype = {
108
109
/**
110
* 执行按需加载图片,并将已加载的图片标记为已加载
111
* @return 无
112
*/
113
load: function() {
114
var iScrollTop = document.documentElement.scrollTop || document.body.scrollTop;
115
// 屏幕上边缘
116
var iClientHeight = document.documentElement.clientHeight + iScrollTop;
117
// 屏幕下边缘
118
var i = 0;
119
var aParent = [];
120
var oParent = null;
121
var iTop = 0;
122
var iBottom = 0;
123
var aNotLoaded = this.loaded(0);
124
if (this.loaded(1).length != this.aImg.length) {
125
var notLoadedLen = aNotLoaded.length;
126
for (i = 0; i <
notLoadedLen
; i++) {
127
iTop
=
API
.pageY(aNotLoaded[i]) - 200;
128
iBottom
=
API
.pageY(aNotLoaded[i]) + aNotLoaded[i].offsetHeight + 200;
129
var isTopArea = (iTop > iScrollTop && iTop <
iClientHeight
) ? true : false;
130
var isBottomArea = (iBottom > iScrollTop && iBottom <
iClientHeight
) ? true : false;
131
if (isTopArea || isBottomArea) {
132
// 把预存在自定义属性中的真实图片地址赋给src
133
aNotLoaded[i]
.src
=
API
.attr(aNotLoaded[i], 'data-src') || aNotLoaded[i].src;
134
if (!API.hasClass(aNotLoaded[i], 'loaded')) {
135
if ('' != aNotLoaded[i].className) {
136
aNotLoaded[i]
.className
=
aNotLoaded
[i].className.concat(" loaded");
137
138
}
139
else {
140
aNotLoaded[i]
.className
=
'loaded'
;
141
142
}
143
}
144
}
145
}
146
}
147
},
148
149
/**
150
* 已加载或者未加载的图片数组
151
* @param {Number} status 图片是否已加载的状态,0代表未加载,1代表已加载
152
* @return Array 返回已加载或者未加载的图片数组
153
*/
154
loaded: function(status) {
155
var array = [];
156
var
i
=
0
;
157
for (
i
=
0
; i < this.aImg.length; i++) {
158
var
hasClass
=
API
.hasClass(this.aImg[i], 'loaded');
159
if (!status) {
160
if (!hasClass)
161
array.push(this.aImg[i])
162
}
163
if (status) {
164
if (hasClass)
165
array.push(this.aImg[i])
166
}
167
}
168
return array;
169
}
170
};
171
// 按需加载初始化
172
API.on(window, 'load', function () {new lazyload()});
173
</script>
174
</
body
>
175
</
html
>
运行上述的示例代码,并抓包会发现:一开始并没有看到图片的请求,但当拉动滚动条到页面下面时,将会看到图片发送请求。目前很多框架都已经支持图片的懒惰加载,平时在开发中,大家可以对图片实现懒惰加载,这是有效提升性能的一个方法,特别是网页图片比较多时,更加应该使用该方法。
按需加载除了上述场景外,还有更多的场景。如下图:
页面一开始,加载的是“全部”标签里面的内容,但在点击“指定商品折扣券”标签时,才去加载对应的图片。实现思路如下:
- 生成<img data-src=”url”>标签时,用data-src来保存图片地址;
- 在点击标签事件时,获取所有图片,图片的src的值用data-src的来替换;
示例代码如下:
01
<!DOCTYPE html>
02
<
html
>
03
<
head
>
04
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=utf-8"
/>
05
<
title
>标签按需加载</
title
>
06
</
head
>
07
08
<
body
>
09
<
style
>
10
ul li {width:200px;height:30px;float:left; list-style:none; text-align:center;}
11
.on{border-top:1px solid #3FF;border-left:1px solid #3FF;border-right:1px solid #3FF; }
12
.hide{display:none;}
13
</
style
>
14
<
ul
>
15
<
li
>全部</
li
>
16
<
li
id
=
"viewTsb1"
onclick
=
"showTabContent()"
>指定商品折扣券</
li
>
17
</
ul
>
18
<
div
style
=
"width:800px;height:500px;clear:both;"
>
19
<
div
id
=
"tab1"
style
=
"height:25px; line-height:25px; margin:50px 0 0 40px"
>全部标签应该展示所有内容</
div
>
20
<
div
id
=
"tab2"
>
21
<
img
width
=
"158"
height
=
"158"
data-src
=
"http://img2.114msn.com/jindian/20081071153761308.jpg"
/>
22
<
img
width
=
"158"
height
=
"158"
data-src
=
"http://www.mjjq.com/blog/photos/Image/mjjq-photos-900.jpg"
/>
23
</
div
>
24
</
div
>
25
<
script
>
26
var isLoadedImg = false;
27
function showTabContent(){
28
if(isLoadedImg){
29
return;
30
}
31
var elem = document.getElementById("tab2");
32
document.getElementById("tab1").className="hide";
33
elem.className="";
34
var arrImage = elem.getElementsByTagName("img");
35
var l = arrImage.length;
36
for(var i=0;i&
l
t;l;i++){
37
arrImage[i]
.src
=
arrImage
[i].getAttribute('data-src');
38
}
39
isLoadedImg
=
true
;
40
//todo 更改标签状态
41
}
42
</script>
43
</
body
>
44
</
html
>
运行上述代码并抓包并发现:一开始没有看到有图片的请求,但点击“指定商品折扣券”标签时,看到有图片的请求发送。需要注意的是,为了确保体验,首屏的图片不建议懒惰加载,而应该直接展示出来;避免一开始用户就无法看到图片,在IE下看到一个虚线框,这样体验反而不好。
按需执行JS
按需执行JS和懒惰加载图片比较类似。当打开网页时,如果等所有JS都加载并执行完毕,再把界面呈现给用户,这样整体上性能会比较慢,体验也不友好。就是当某个动作触发后,再执行相应的JS,以便来渲染界面。按需执行JS,可以应用在下列场景:执行一些耗时比较久的JS代码,或执行JS后,需要加载比较多图片、加载iframe、加载广告等。在一些webapp的应用中,或比较复杂的页面时,更加应该使用这种方法。
实现思路和按需加载比较类似:
- 对滚动条进行事件绑定,假设绑定的函数为function lazyExecuteJS(){};
- 在函数lazyExecuteJS中,按照下面思路实现:选择一个元素作为参照物,当滚动条即将靠近时该元素位置,开始执行对应的JS,从而实现对界面的渲染;
示例代码如下(以YUI3框架为例):
首先下载最近封装的异步滚动条加载组件:Y.asyncScrollLoader,然后运行下面的代码(需要把页面和Y.asyncScrollLoader.js 放在同一个目录):
01
<!DOCTYPE html>
02
<
html
>
03
<
head
>
04
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=utf-8"
/>
05
<
title
>按需执行JS</
title
>
06
</
head
>
07
<
script
src
=
"http://yui.yahooapis.com/3.2.0/build/yui/yui-debug.js"
></
script
>
08
<
body
>
09
<
div
style
=
"height:1500px"
><
span
style
=
""
>向下拖动滚动条,拉到广告条的位置,将会自动加载广告的内容!</
span
></
div
>
10
<
div
id
=
"ADContent"
>这里是广告的内容,将会用JS进行填充</
div
>
11
<
script
>
12
YUI({modules:{'asyncScrollLoader': {
13
fullpath: 'Y.asyncScrollLoader.js',
14
type: 'js',
15
requires:['widget']
16
}
17
}}).use('widget','asyncScrollLoader', "node", function(Y){
18
var loadAD = function(){
19
//这里可以用简单的代码代替,实际项目中,可以执行任何JS代码,如请求CGI,或者广告数据,然后再填充到页面上
20
var html = '<
div
><
img
src
=
"http://t2.baidu.com/it/u=2027994158,3223530939&fm=23&gp=0.jpg"
alt
=
""
/><
span
>哈哈,我也是动态加载进来的,可以从html结构或抓包看出\效果哈!</
span
><\/div>'
21
Y.one('#ADContent').set('innerHTML',html);
22
}
23
24
var cfg = {
25
'elementName' : 'div',
26
'className' : 'lazy-load',
27
'contentAttribute' : '' ,
28
'foldDistance': 10,
29
'obCallback' : {
30
'funName' : loadAD,
31
'argument' : [],
32
'context' : this
33
}
34
};
35
36
new Y.asyncScrollLoader(cfg).renderer();
37
});
38
</
script
>
39
</
body
>
40
</
html
>
运行上述代码并抓包发现:打开页面时,是不没有看到有对应的图片请求,但当滚动条拉到一定位置时,loadAD的函数被执行。
按需加载JS
JavaScript无非就是script标签引入页面,但当项目越来越大的时候,单页面引入N个js显然不行,合并为单个文件减少了请求数,但请求的文件体积却很大。这时候比较合理的做法就是按需加载。按需加载和按需执行JS比较类似,只不过要执行的JS变成了固定的“实现加载JS”的代码。按需加载实现的思路如下:
- 对滚动条进行事件绑定,假设绑定的函数为function lazyLoadJS(){};
- 在函数lazyLoadJS中,按照下面思路实现:选择一个元素作为参照物,当滚动条即将靠近时该元素位置,开始执行加载对应JS;
- 在JS加载完毕后,开始执行相应的函数来渲染界面;
- 在实际项目中,可以根据需要设置一个目标距离,比如还有200像素该元素即将进入可视区域;按需加载JS和按需执行JS比较类似,这里就不再单独提供示例代码了;大家可以在按需执行JS的中示例中,把loadAD函数更改为动态加载JS即可;
分屏展示
当一个网页比较长,有好几个屏幕,而且加载了大量的图片、广告等资源文件时,分屏展示,可提升页面性能和用户体验。其实分屏展示也可以从按需加载的的角度来看待,默认是加载第一屏幕的内容,当滚动条拉动即将到达下一个屏幕时,再开始渲染下个屏的内容。换言之,是把图片、背景图片、HTML一起按需加载,一开始不对HTML进行解析,那么背景图、img图片也不会进行加载。
分屏展示的思路如下:
- 根据具体业务情况,收集主流最大的分辨率的高度;假设这里是用960px;
- 按照这个高度进行分屏,依次把下一个屏幕内的HTML用<textarea>HTML</’textarea> 来表示;
- 为了让页面的高度不变,需要让textarea占据一定的页面空间,也就是让页面出现对应的滚动条;因此需要指定样式visility:hidden,并指定它的高度和宽度。
- 利用上述讲的按需执行JS,把<textarea>HTML</’textarea>里面的HTML代码提取出来,重新填充到textarea的父节点上,便可实现解析对应HTML,从而实现分屏展示。
示例代码如下(需要把页面和Y.asyncScrollLoader.js 放在同一个目录):
01
<!DOCTYPE html>
02
<
html
>
03
<
head
>
04
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=utf-8"
/>
05
<
title
>网页分屏展示</
title
>
06
</
head
>
07
<
style
>
08
.class2 {
09
visibility:hidden;
10
widh:100%;
11
height:300px;
12
}
13
</
style
>
14
<
script
src
=
"http://yui.yahooapis.com/3.2.0/build/yui/yui-debug.js"
></
script
>
15
<
body
>
16
<
div
style
=
"height:1500px"
><
span
style
=
""
>向下拖动滚动条,美图在下面!</
span
></
div
>
17
<
textarea
>
18
<
div
>
19
<
img
src
=
"http://travel.shangdu.com/liu/uploads/allimg/090407/1521221.jpg"
alt
=
""
/><
span
style
=
"font-size:16px;color:red;"
>我是动态加载进来的,可以从html结构或抓包看出效果哈!</
span
>
20
</
div
>
21
</
textarea
>
22
<
div
style
=
"height:800px"
><
span
style
=
""
>下面继续上美图!</
span
></
div
>
23
<
textarea
>
24
<
div
>
25
<
img
src
=
"http://download.99sucai.com/upload/images/201006042009245.jpg"
alt
=
""
/><
span
>哈哈,我也是动态加载进来的,可以从html结构或抓包看出效果哈!</
span
>
26
</
div
>
27
</
textarea
>
28
<
script
>
29
YUI({modules:{'asyncScrollLoader': {
30
fullpath: 'Y.asyncScrollLoader.js',
31
type: 'js',
32
requires:['widget']
33
}
34
}}).use('widget','asyncScrollLoader', "node", function(Y){
35
var cfg = {
36
'elementName' : 'textarea',
37
'className' : 'class2',
38
'contentAttribute' : 'value' ,
39
'foldDistance': 10
40
};
41
new Y.asyncScrollLoader(cfg).renderer();
42
});
43
</
script
>
44
</
body
>
45
</
html
>
运行上面代码并抓包发现:在默认首屏,并没有去解析textarea里面的代码,但当拉动滚动条到一定位置时,textarea里面的HTML依次被解析,从而实现了网页分屏展示。上述的示例代码,可以在这里下载来查看: 示例代码
使用“按需加载”进行性能优化时,需要合理选择触发的动作。“按需加载”的最大优势在于减少了不必要的资源请求,节省流量,真正实现“按需所取”。但是“按需加载”本身如果使用不当也会影响用户体验,因为“按需加载”的时机在用户触发某动作之后,如果用户的网速比较慢的话,加载脚本或执行脚本可能需要等候较长的时间,而用户则不得不为此付出代价。因此,如果要使用“按需加载”则需要选择正确的触发动作,如果是根据滚动条来触发,可考虑一个目标距离,假设目标距离还有200像素即将进入可视区域,则就开始加载,而不是等到进入了可视区域才加载。以上所讲的各种“按需加载”类型,都可以封装成相应的组件,然后就可以在项目中进行应用。
- “按需加载”的应用
- “按需加载”的应用
- “按需加载”的应用
- “按需加载”的应用
- “按需加载”的应用
- 类加载器的应用
- 懒加载ImageLoader的应用
- Android应用开发之(按需加载View)
- java网络加载协议JNLP的应用
- “加载图片gif”的一个应用
- android应用的loading加载动画制作
- 图片延迟加载技术-Lazyload的应用
- 图片延迟加载技术-Lazyload的应用
- Android Launcher应用的加载过程
- Log4j日志文件的加载及应用
- SuperMap iServer应用—地图的加载
- 【Android】应用的loading加载动画制作
- 关于应用启动页的加载
- 读取 SQL 脚本并执行
- OC基础 便利构造器 继承,
- myeclipse手动安装插件-自用
- jQuery操作Select
- 【SSH三大框架】Hibernate基础第十三篇:lazy、constrained、fetch三个属性的作用和使用方法
- “按需加载”的应用
- 【计算几何初步-凸包-Jarvis步进法。】【HDU1392】Surround the Trees
- jQuery获取Select元素,并选择的Text和Value:
- 人品计算器
- makefile 的 ifdef, ifeq 使用及辨析
- 第14周--多科成绩单(2)
- 多线程文件下载
- 乾隆年间贪污贿赂成风:皇帝敛财不逊臣子
- 优化Android应用内存的若干方法