머리말
Imagepool은 이미지 로딩을 관리하는 JS 도구로, 동시에 로드되는 이미지 수를 제어할 수 있습니다.
이미지 로딩의 경우 가장 원시적인 방법은 과 같은 img 태그를 직접 작성하는 것입니다.
지속적인 최적화 후에 이미지에 대한 지연 로딩 솔루션이 등장했습니다. 이번에는 이미지의 URL이 src 속성에 직접 기록되지 않고 다음과 같은 특정 속성에 기록됩니다. . 이렇게 하면 브라우저가 이미지를 적절한 시기에 로드해야 하는 경우 js를 사용하여 img 태그의 src 속성에 data-src 속성에 URL을 넣거나 URL을 읽은 후 사용합니다. , js를 사용하여 이미지를 로드하고, 로드가 완료된 후 src 속성을 설정하고 이미지를 표시합니다.
잘 통제되고 있는 것 같지만, 여전히 문제가 있습니다.
사진의 일부만 로드할 수도 있지만 사진의 이 부분은 여전히 상대적으로 큰 크기일 수 있습니다.
PC 쪽에서는 별 문제가 아니지만, 모바일 쪽에서는 너무 많은 이미지가 동시에 로딩되어 애플리케이션이 다운될 가능성이 매우 높습니다.
따라서 이미지 로딩 동시성을 제어하기 위한 이미지 버퍼링 메커니즘이 시급히 필요합니다. 백엔드 데이터베이스 연결 풀과 마찬가지로 너무 많은 연결을 생성하지 않으며 각 연결을 완전히 재사용할 수 있습니다.
이때 이미지풀이 탄생했습니다.
불량한 설계도
사용 지침
먼저 연결 풀을 초기화해야 합니다.
var imagepool = initImagePool(5);
initImagePool은 전역 메서드이며 어디에서나 직접 사용할 수 있습니다. 연결 풀을 생성하는 기능이며 연결 풀에 최대 연결 수를 지정할 수 있으며 선택 사항이며 기본값은 5입니다.
동일한 페이지에서 initImagePool을 여러 번 호출하면 항상 첫 번째 인스턴스인 동일한 핵심 인스턴스가 반환되므로 약간 싱글톤처럼 느껴집니다. 예:
var imagepool1 = initImagePool(3);
var imagepool2 = initImagePool(7);
이때, imagepool1과 imagepool2 모두 최대 연결 수는 3개이며, 내부적으로 동일한 Core 인스턴스가 사용됩니다. 내부 코어는 동일하지만 imagepool1 === imagepool2는 아닙니다.
초기화 후에는 안심하고 이미지를 불러올 수 있습니다.
가장 간단한 호출 방법은 다음과 같습니다.
var imagepool = initImagePool(10);
imagepool.load("이미지 URL",{
성공: 함수(src){
console.log("성공:::::" src);
},
오류: 함수(src){
console.log("error:::::" src);
}
});
인스턴스에서 직접 로드 메소드를 호출하면 됩니다.
로드 메소드에는 두 개의 매개변수가 있습니다. 첫 번째 매개변수는 로드해야 하는 이미지 URL이고, 두 번째 매개변수는 성공 및 실패 콜백을 포함한 다양한 옵션이며, 콜백 중에 이미지 URL이 전달됩니다.
이렇게 하면 사진 한 장만 전달할 수 있기 때문에 다음과 같은 형태로 쓸 수도 있습니다.
var imagepool = initImagePool(10);
imagepool.load(["image1url","image2url"],{
성공: 함수(src){
console.log("성공:::::" src);
},
오류: 함수(src){
console.log("error:::::" src);
}
});
By passing in an image url array, you can pass in multiple images.
Every time the image is loaded successfully (or fails), the success (or error) method will be called and the corresponding image URL will be passed in.
But sometimes we don’t need such frequent callbacks. We just pass in an array of image URLs and call back after all the images in this array have been processed.
Just add an option:
var imagepool = initImagePool(10);
imagepool.load(["image1url ","image2url "],{
Success: function(sArray, eArray, count){
console.log("sArray:::::" sArray);
console.log("eArray:::::" eArray);
console.log("count:::::" count);
},
error: function(src){
console.log("error:::::" src);
},
Once: true
});
By adding a once attribute to the option and setting it to true, you can achieve only one callback.
This callback will inevitably call back the success method. At this time, the error method is ignored.
At this time, when calling back the success method, instead of passing in an image url parameter, three parameters are passed in, namely: successful url array, failed url array, and the total number of images processed.
In addition, there is a method to obtain the internal status of the connection pool:
var imagepool = initImagePool(10);
console.log(imagepool.info());
By calling the info method, you can get the internal status of the connection pool at the current moment. The data structure is as follows:
Object.task.count The number of tasks waiting to be processed in the connection pool
Object.thread.count Maximum number of connections in the connection pool
Object.thread.free Number of idle connections in the connection pool
It is recommended not to call this method frequently.
The last thing to note is that if the image fails to load, it will be tried up to 3 times. If the loading fails in the end, the error method will be called back. The number of attempts can be modified in the source code.
Finally, I would like to emphasize that readers can push images into the connection pool as much as they want without worrying about excessive concurrency. Imagepool will help you load these images in an orderly manner.
Last but not least, it must be noted that imagepool theoretically will not reduce the image loading speed, it is just a gentle loading.
Source code
(function(exports){
//Single case
var instance = null;
var emptyFn = function(){};
//Initial default configuration
var config_default = {
//The number of thread pool "threads"
Thread: 5,
//Number of retries for image loading failure
//Retry 2 times, plus the original one, a total of 3 times
"try": 2
};
//Tools
var _helpers = {
//Set dom attributes
setAttr: (function(){
var img = new Image();
//Determine whether the browser supports HTML5 dataset
if(img.dataset){
return function(dom, name, value){
dom.dataset[name] = value;
return value;
};
}else{
return function(dom, name, value){
dom.setAttribute("data-" name, value);
return value;
};
}
}()),
//Get dom attributes
getAttr: (function(){
var img = new Image();
//Determine whether the browser supports HTML5 dataset
if(img.dataset){
return function(dom, name){
Return dom.dataset[name];
};
}else{
return function(dom, name){
return dom.getAttribute("data-" name);
};
}
}())
};
/**
*Construction method
* @param max The maximum number of connections. numerical value.
*/
Function ImagePool(max){
//Maximum number of concurrencies
This.max = max || config_default.thread;
This.linkHead = null;
This.linkNode = null;
//Loading pool
//[{img: dom,free: true, node: node}]
//node
//{src: "", options: {success: "fn",error: "fn", once: true}, try: 0}
This.pool = [];
}
/**
* Initialization
*/
ImagePool.prototype.initPool = function(){
var i,img,obj,_s;
_s = this;
for(i = 0;i < this.max; i ){
obj = {};
img = new Image();
_helpers.setAttr(img, "id", i);
img.onload = function(){
var id,src;
//回调
//_s.getNode(this).options.success.call(null, this.src);
_s.notice(_s.getNode(this), "success", this.src);
//处理任务
_s.executeLink(this);
};
img.onerror = function(e){
var node = _s.getNode(this);
//判断尝试次数
if(node.try < config_default.try){
node.try = node.try 1;
//再次追加到任务链表末尾
_s.appendNode(_s.createNode(node.src, node.options, node.notice, node.group, node.try));
}else{
//error回调
//node.options.error.call(null, this.src);
_s.notice(node, "error", this.src);
}
//处理任务
_s.executeLink(this);
};
obj.img = img;
obj.free = true;
this.pool.push(obj);
}
};
/**
* Callback encapsulation
* @param node node. object.
* @param status status. String. Optional values: success(success)|error(failure)
* @param src image path. String.
*/
ImagePool.prototype.notice = function(node, status, src){
node.notice(status, src);
};
/**
* * Processing linked list tasks
* @param dom image dom object. object.
*/
ImagePool.prototype.executeLink = function(dom){
//判断链表是否存在节点
if(this.linkHead){
//加载下一个图片
this.setSrc(dom, this.linkHead);
//연결리스트 헤더 제거
This.shiftNode();
}그 외{
// 자신의 상태를 유휴 상태로 설정
This.status(dom, true);
}
};
/**
* 유휴 "스레드" 가져오기
*/
ImagePool.prototype.getFree = function(){
변수 길이,i;
for(i = 0, 길이 = this.pool.length; i
If(this.pool[i].free){
return this.pool[i];
}
}
null을 반환합니다.
};
/**
* src 속성 설정 캡슐화
* src 속성을 변경하는 것은 이미지를 로드하는 것과 동일하므로 작업이 캡슐화됩니다
* @param dom 이미지 dom 객체. 물체.
* @param 노드 노드. 물체.
*/
ImagePool.prototype.setSrc = 함수(dom, 노드){
//풀의 "스레드"를 유휴 상태가 아닌 상태로 설정
This.status(dom, false);
//연관 노드
This.setNode(dom, node);
//이미지 로드
dom.src = node.src;
};
/**
* 풀의 "스레드" 상태 업데이트
* @param dom 이미지 dom 객체. 물체.
* @param 상태 상태. 부울. 선택 값: true(유휴) | false(유휴 아님)
*/
ImagePool.prototype.status = 함수(dom, 상태){
var id = _helpers.getAttr(dom, "id");
This.pool[id].free = 상태;
//유휴 상태, 관련 노드 지우기
if(상태){
This.pool[id].node = null;
}
};
/**
* 풀에 있는 "스레드"의 관련 노드를 업데이트하세요
* @param dom 이미지 dom 객체. 물체.
* @param 노드 노드. 물체.
*/
ImagePool.prototype.setNode = 함수(dom, 노드){
var id = _helpers.getAttr(dom, "id");
This.pool[id].node = 노드;
return this.pool[id].node === node;
};
/**
* 풀에서 "스레드"의 관련 노드를 가져옵니다
* @param dom 이미지 dom 객체. 물체.
*/
ImagePool.prototype.getNode = 함수(dom){
var id = _helpers.getAttr(dom, "id");
return this.pool[id].node;
};
/**
* 외부 인터페이스, 사진 로딩
* @param src는 src 문자열이거나 src 문자열의 배열일 수 있습니다.
* @param 옵션 사용자 정의 매개변수. 포함: 성공 콜백, 오류 콜백, 일회 식별자.
*/
ImagePool.prototype.load = 함수(src, 옵션){
var srcs = [],
무료 = null,
길이 = 0,
i = 0,
//Only initialize the callback strategy once
notice = (function(){
If(options.once){
Return function(status, src){
var g = this.group,
o = this.options;
//Record
g[status].push(src);
//Determine whether the reorganization has been completed
If(g.success.length g.error.length === g.count){
//Actually, it is executed separately as another task to prevent the callback function from taking too long to affect the image loading speed
setTimeout(function(){
o.success.call(null, g.success, g.error, g.count);
},1);
}
};
}else{
Return function(status, src){
var o = this.options;
//Direct callback
setTimeout(function(){
o[status].call(null, src);
},1);
};
}
}()),
group = {
Count: 0,
Success: [],
error: []
},
node = null;
options = options || {};
options.success = options.success || emptyFn;
options.error = options.error ||
srcs = srcs.concat(src);
//그룹 요소 개수 설정
group.count = srcs.length;
//로드해야 하는 이미지를 탐색합니다
for(i = 0, 길이 = srcs.length; i
//노드 생성
노드 = this.createNode(srcs[i], 옵션, 공지, 그룹);
//스레드 풀이 사용 가능한지 확인
무료 = this.getFree();
if(무료){
//시간 있으면 바로 이미지 로딩
This.setSrc(free.img, node);
}그밖에{
//유휴 시간이 없습니다. 연결 목록에 작업을 추가하세요
This.appendNode(노드);
}
}
};
/**
* 내부 상태 정보 확인
* @returns {{}}
*/
ImagePool.prototype.info = function(){
var 정보 = {},
길이 = 0,
i = 0,
노드 = null;
//스레드
info.thread = {};
//총 스레드 수
info.thread.count = this.pool.length;
//유휴 스레드 수
info.thread.free = 0;
//작업
info.task = {};
//처리할 작업 개수
info.task.count = 0;
//유휴 "스레드" 수를 가져옵니다
for(i = 0, 길이 = this.pool.length; i
If(this.pool[i].free){
info.thread.free = info.thread.free 1;
}
}
//작업 수 가져오기(작업 체인 길이)
노드 = this.linkHead;
if(노드){
info.task.count = info.task.count 1;
동안(node.next){
info.task.count = info.task.count 1;
노드 = node.next;
}
}
반품 정보;
};
/**
* 노드 생성
* @param src 이미지 경로. 끈.
* @param 옵션 사용자 정의 매개변수. 포함: 성공 콜백, 오류 콜백, 일회 식별자.
* @param 공지 콜백 전략. 기능.
* @param 그룹 그룹 정보입니다. 물체. {횟수: 0, 성공: [], 오류: []}
* @param tr 오류 재시도 횟수입니다. 수치. 기본값은 0입니다.
* @returns {{}}
*/
ImagePool.prototype.createNode = 함수(src, 옵션, 공지, 그룹, tr){
var 노드 = {};
Node.src = src;
Node.options = 옵션;
node.notice = 공지;
Node.group = 그룹;
node.try = tr ||
반환 노드;
};
/**
* 작업 목록 끝에 노드를 추가하세요
* @param 노드 노드. 물체.
*/
ImagePool.prototype.appendNode = 함수(노드){
//연결리스트가 비어 있는지 확인
if(!this.linkHead){
This.linkHead = 노드;
This.linkNode = 노드;
}그 외{
This.linkNode.next = 노드;
This.linkNode = 노드;
}
};
/**
* 연결리스트 헤더 삭제
*/
ImagePool.prototype.shiftNode = function(){
//연결리스트에 노드가 있는지 확인
if(this.linkHead){
//링크드 리스트 헤더 수정
This.linkHead = this.linkHead.next ||
}
};
/**
* 외부 인터페이스 내보내기
* @param max 최대 연결 수입니다. 수치.
* @returns {{load: 함수, info: 함수}}
*/
imports.initImagePool = 함수(최대){
if(!instance){
인스턴스 = 새 ImagePool(최대);
인스턴스.initPool();
}
반품 {
/**
* 사진 로딩
*/
로드: 함수(){
Instance.load.apply(인스턴스, 인수);
},
/**
*내부정보
* @returns {*|any|void}
*/
정보: 기능(){
return instance.info.call(인스턴스);
}
};
};
}(이));
위는 이 훌륭한 JavaScript 프런트엔드 이미지 로딩 관리자를 사용하는 방법에 대한 예입니다.