持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情
封装一个比较好用,功能完善,代码健壮的功能其实不是一个简单的事情,今天用一个图片预加载的功能需求来演示如何封装。
功能需求
- 加载的图片可以是一个数组或一个对象,格式如下
var images = ['xxx.png', 'yyy.jpg']
var images = {
p1: 'xxx.png',
p2: 'yyy.png'
}
- 全部加载完成后需要给用户一个提示,即让用户传入一个回调函数
function loadImage(images, callback) {
// 如果全部加载完成,则调用回调函数,并传入一个参数
callback && callback(true)
}
- 支持如果超过一定的时间,则判断加载超时
function loadImage(images, callback, timeout) {
var timeoutId
var isTimeout
if (timeout) {
timeoutId = setTimeout(onSetTimeout, timeout)
}
function onSetTimeout() {
isTimeout = true
// false 表示加载失败
callback(false)
}
}
代码实现
function loadImage(images, callback, timeout) {
// 加载图片的计数器
var count = 0
// 全部图片加载成功的一个标志位
var success = true
// 超时timer的id
var timeoutId = 0
// 是否加载超时的标志位
var isTimeout = false
// 对图片数据或对象进行遍历
for (var key in images) {
// 过滤prototype上的属性
if (!images.hasOwnProperty(key)) {
continue
}
// 获得每个图片元素
// 期望格式是个object: {src: xxxx}
var item = images[key]
if (typeof item === 'string') {
item = images[key] = {
src: item
}
}
// 如果格式不满足要求,则丢弃,遍历下一条数据
if (!item || !item.src) {
continue
}
// 计数器+1
count++
// 设置图片id,保证每次调用id都不同
item.id = '__img__' + key + getId()
// 设置图片元素的img,它是一个img对象
item.img = window[item.id] = new Image()
doLoad(item)
}
// 如果images是一个空数组或空对象,直接执行回调
if (!count) {
callback(success)
} else if (timeout) {
// 如果用户传递了超时时间
timeoutId = setTimeout(onTimeout, timeout)
}
/**
* 加载图片
* @param item
*/
function doLoad(item) {
item.status = 'loading'
var img = item.img
// 定义图片加载成功或者失败的回调函数
img.onload = function () {
success = success && true
item.status = 'loaded'
done()
}
img.onerror = function () {
success = false
item.status = 'error'
done()
}
// 发起一个http的图片请求
img.src = item.src
/**
* 每张图片加载完成的回调函数
*/
function done() {
// 清理事件
img.onload = img.onerror = null
try {
delete window[item.id]
} catch (e) {}
// 每张图片加载完成,计数器减一,当所有图片加载完毕且没有超时的情况下,
// 清除超时计时器,且执行回调函数
if (!--count && !isTimeout) {
clearTimeout(timeoutId)
callback(success)
}
}
}
function onTimeout() {
isTimeout = true
callback(false)
}
}
var __id = 0
function getId() {
return __id++
}
总结
上面的代码非常清晰,可以总结出以下几点供自己以后借鉴:
- 支持多张图片的预加载:支持传入一个数组和对象,利用
count变量来判断是否全部加载完成; - 支持用户传入回调函数:通过回调函数用户可以在所有图片加载完成或者失败之后,做一些自定义的逻辑操作;
- 支持自定义的超时功能:通过传入一个请求时长,利用
setTimeout来对比是否超时; - 代码考虑周全:当图片加载成功或者失败后,都会清除事件等,防止内存泄漏;
- 逻辑划分清晰:每一个单一功能都封装成一个函数调用;