懶加載和預加載的實現
提到前端性能優化中圖片資源的優化,懶加載和預加載就不能不說,下麵我用最簡潔明了的語言說明懶加載和預加載的核心要點以及實現
懶加載
什麼是懶加載
懶加載也就是延遲加載;當訪問一個頁麵時,先將img標簽中的src鏈接設為同一張圖片(這樣就隻需請求一次,俗稱占位圖),將其真正的圖片地址存儲在img標簽的自定義屬性中(比如data-src);當js監聽到該圖片元素進入可視窗口時,即將自定義屬性中的地址存儲到src屬性中,達到懶加載的效果;這樣做能防止頁麵一次性向服務器響應大量請求導致服務器響應慢頁麵卡頓或崩潰等問題
為什麼要使用懶加載
懶加載對於圖片較多頁麵很長的業務場景很適用,可以減少無效資源的加載
懶加載的實現步驟
1.首先,不要將圖片地址放到src屬性中,而是放到其它屬性(data-src)中
2.頁麵加載完成後,根據scrollTop判斷圖片是否在用戶的視野內,如果在,則將data-original屬性中的值取出存放到src屬性中
3.在滾動事件中重複判斷圖片是否進入視野;如果進入,則將data-original屬性中的值取出存放到src屬性中
代碼實現:
既然懶加載的原理是基於判斷元素是否出現在窗口可視範圍內,首先我們寫一個函數判斷元素是否出現在可視範圍內:
<script>
function isVisible($node){
var winH = $(window).height(),
scrollTop = $(window).scrollTop(),
offSetTop = $(window).offSet().top;
if (offSetTop < winH + scrollTop) {
return true;
} else {
return false;
}
}
</script>
再添加上瀏覽器的事件監聽函數,讓瀏覽器每次滾動就檢查元素是否出現在窗口可視範圍內:
<script>
$(window).on("scroll", function{
if (isVisible($node)){
console.log(true);
}
})
</script>
現在我們要做的是,讓元素隻在第一次被檢查到時打印true,之後就不再打印了
<script>
var hasShowed = false;
$(window).on("sroll",function{
if (hasShowed) {
return;
} else {
if (isVisible($node)) {
hasShowed = !hasShowed;
console.log(true);
}
}
})
</script>
這樣我們就實現了懶加載
利用懶加載和AJAX,我們還可以實現無限滾動查看時間線/在滾動頁麵一段距離後出現回到頂部按鈕的效果
懶加載的優點
顯著的提高頁麵加載速度,又不下載多餘的資源節省了流量;同時更少的圖片並發請求數也可以減輕服務器的壓力
懶加載插件
關於圖片延時加載,網上有很多應用的例子以及插件;目前研究過的兩個插件分別是jquery插件lazyload.js和原生js插件echo.js;二者的區別不用說,jquery插件使用的時候必須引入jquery才可以,且該插件功能強大,靈活性也高;而echo.js是用原生寫的插件,代碼少,不依賴其他庫,拿起來就可以用,但能夠實現的效果不如lazyload豐富強大,但基本的延時加載要求都能滿足
jquery.lazyload.js
如何使用
延遲加載依賴於於jQuery,第一步引入文件:
<script src ="jQuery.js"></script>
<script src="jQuery.lazyload.js"></script>
接下來修改html的一些屬性:圖像的src地址暫時存儲在自定義屬性data-original中,然後給需要延時加載圖像加上一個特定的類,類的名字由你自己決定,使用的時候統一類名即可;為這些圖片綁定延時加載:
<img src="img/grey.gif" data-original="img/example.jpg" width="640" heigh="480">
用的時候就像下麵:
$("img.lazy").lazyload();
所有class為lazy的圖片將被延遲加載
注意:必須設置圖像的尺寸,寬度和高度屬性或CSS,否則插件可能無法正常工作
參數設置
1.設置閾值
默認情況下圖片在位於可視區域後才開始加載;如果想提前加載圖片,可通過設置threshold的值來改變其顯示的時間,設置threshold為200使圖片在距離屏幕可見區域下方200像素時就開始加載
$("img.lazy").lazyload({
threshold:200
});
2.事件觸發加載
默認是scoll事件觸發延時加載,即等到用戶向下滾動至圖片出現在屏幕可視區域時圖片才能加載,但可以使用jQueryclick或mouseover等事件觸發圖片的加載,也可以使用自定義事件,實現隻有當用戶點擊圖片圖片才能夠加載時可以這樣寫:
$("img.lazy").lazyload({
event : "click"
});
注意:你也可以使用這個技巧延遲圖片加載,即加載前延遲5秒後再加載圖片;就像下麵這樣(trigger()方法觸發被選元素的指定事件類型):
$(function() {
$("img.lazy").lazyload({
event:"click"
});
});
$(window).bind("load", function() {
var timeout = setTimeout(function() {
$("img.lazy").trigger("click") //trigger()方法觸發被選元素的指定事件類型
}, 5000);
});
3.使用特殊效果加載圖片
插件默認使用show()方法顯示圖片;當然你可以使用任何你想用的特效來處理,例如使用fadeIn效果:
$("img.lazy").lazyload({
effect : "fadeIn" //.effect()方法對一個元素應用了一個命名的動畫 特效
});
4.為非JavaScript瀏覽器回退
<img src="img/grey.gif" data-original="img/example.jpg" width="640" heigh="480">
<noscript><img src="img/example.jpg" width="640" heigh="480"></noscript>
可以通過CSS隱藏占位符
.lazy {
display: none;
}
在支持JavaScript的瀏覽器中必須在DOM ready時將占位符顯示出來,這可以在插件初始化的同時完成
$("img.lazy").show().lazyload();
這些都是可選的,但如果你希望插件平穩降級這些都是應該做的
5.圖片內容器
可以將插件用在可滾動容器的圖片上,例如帶滾動條的DIV元素;將容器定義為jQuery對象並作為參數傳到初始化方法裏麵
#container {
height: 600px;
overflow: scroll;
}
$("img.lazy").lazyload({
container: $("#container")
});
6.當圖像並不是連續的
滾動頁麵時,Lazy Load會循環加載圖片;在循環中檢測圖片是否在可視區域內,默認情況下在找到第一張不在可見區域的圖片時停止循環;圖片被認為是流式分布的,圖片在頁麵中的次序和HTML代碼中次序相同;但是在一些布局中,這樣的假設是不成立的;不過你可以通過failurelimit選項來控製加載行為
$("img.lazy").lazyload({
failure_limit : 10
});
將failurelimit設為10令插件找到10個不在可見區域的圖片是才停止搜索
7.處理看不見圖像
可能在你的頁麵上有很多隱藏的圖片;為了提升性能,Lazy Load默認忽略了隱藏圖片;如果想要加載隱藏圖片,將skip_invisible設為 false:
$("img.lazy").lazyload({
skip_invisible : false
});
echo.js
兼容性:Echo.js使用了HTML5的date屬性,並且需要獲取該屬性的值,所以它並不兼容IE6/IE7
使用方法:
引入文件
<script src="js/echo.min.js"></script>
HTML
<img src="images/blank.gif" alt="pic" data-echo="img/pic.jpg" width="640" height="480">
blank.gif用做默認圖片,data-echo的屬性值是圖片的真實地址,同樣最好給圖片設置寬度和高度
JavaScript
echo.init({
offset: 0,
throttle: 0
});
參數說明
echo.js隻有兩個參數:offset和throttle
offset:設置圖片在離可視區域下方在一定距離後開始被加載
throttle:設置圖片延遲多少毫秒後加載
那麼上麵的代碼的意思就是一旦圖片進入可視區域就立即加載
怎麼樣,使用起來真的很簡單吧
預加載
什麼是預加載
提前加載圖片,當用戶需要查看時可直接從本地緩存中渲染
為什麼要使用預加載
圖片預先加載到瀏覽器中,訪問者可順利地在網站上衝浪,並享受到極快的加載速度;這對圖片占據很大比例的網站來說十分有利,保證了圖片快速/無縫地發布,也可幫助用戶在瀏覽網站內容時獲得更好的用戶體驗
預加載的核心要點如下:
圖片等靜態資源在使用前提前請求;資源後續使用時可以從緩存中加載,提升用戶體驗;頁麵展示的依賴關係維護(必需的資源加載完才可以展示頁麵,防止白屏等)
實現預加載主要有三個方法:
用CSS和JavaScript實現預加載
實現預加載圖片有很多方法,包括使用CSS/JavaScript/兩者的各種組合,這些技術可根據不同設計場景設計出相應的解決方案,十分高效
1.單純使用CSS,可容易/高效地預加載圖片,代碼如下:
#preload-01 { background: url(https://domain.tld/image-01.png) no-repeat -9999px -9999px; }
#preload-02 { background: url(https://domain.tld/image-02.png) no-repeat -9999px -9999px; }
#preload-03 { background: url(https://domain.tld/image-03.png) no-repeat -9999px -9999px; }
將這三個ID選擇器應用到(X)HTML元素中,我們便可通過CSS的background屬性將圖片預加載到屏幕外的背景上;隻要這些圖片的路徑保持不變,當它們在Web頁麵的其他地方被調用時,瀏覽器就會在渲染過程中使用預加載(緩存)的圖片;簡單/高效,不需要任何JavaScript
該方法雖然高效,但仍有改進餘地;使用該法加載的圖片會同頁麵的其他內容一起加載,增加了頁麵的整體加載時間;為了解決這個問題,我們增加了JS代碼來推遲預加載的時間,直到頁麵加載完畢;代碼如下:
function preloader() {
if (document.getElementById) {
document.getElementById("preload-01").style.background = "url(https://domain.tld/image-01.png) no-repeat -9999px -9999px";
document.getElementById("preload-02").style.background = "url(https://domain.tld/image-02.png) no-repeat -9999px -9999px";
document.getElementById("preload-03").style.background = "url(https://domain.tld/image-03.png) no-repeat -9999px -9999px";
}
}
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);
在腳本的第一部分,我們獲取使用類選擇器的元素並為其設置了background屬性,以預加載不同的圖片;腳本的第二部分,我們使用addLoadEvent()函數來延遲preloader()函數的加載時間,直到頁麵加載完畢;如果JavaScript無法在用戶的瀏覽器中正常運行,會發生什麼?很簡單,圖片不會被預加載,當頁麵調用圖片時,正常顯示即可
2.僅使用JavaScript實現預加載
上述方法有時確實很高效,但我們逐漸發現它在實際實現過程中會耗費太多時間;相反,我更喜歡使用純JavaScript來實現圖片的預加載;下麵將提供兩種這樣的預加載方法,它們可以很漂亮地工作於所有現代瀏覽器之上
加載方法1:
隻需簡單編輯/加載所需要圖片的路徑與名稱即可,很容易實現:
<div >
<script type="text/javascript">
var images = new Array()
function preload() {
for (i = 0; i < preload.arguments.length; i++) {
images[i] = new Image()
images[i].src = preload.arguments[i]
}
}
preload(
"https://domain.tld/gallery/image-001.jpg",
"https://domain.tld/gallery/image-002.jpg",
"https://domain.tld/gallery/image-003.jpg"
)
</script>
</div>
該方法尤其適用預加載大量的圖片
加載方法2:
該方法與上麵的方法類似,也可以預加載任意數量的圖片;將下麵的腳本添加入任何Web頁中,根據程序指令進行編輯即可
<div >
<script type="text/javascript">
if (document.images) {
img1 = new Image();
img2 = new Image();
img3 = new Image();
img1.src = "https://domain.tld/path/to/image-001.gif";
img2.src = "https://domain.tld/path/to/image-002.gif";
img3.src = "https://domain.tld/path/to/image-003.gif";
}
</script>
</div>
正如所見,每加載一個圖片都需要創建一個變量,如"img1=new Image();",及圖片源地址聲明,如"img3.src='../path/to/image-003.gif';";參考該模式,可根據需要加載任意多的圖片;我們又對該方法進行了改進,將該腳本封裝入一個函數中,並使用addLoadEvent()延遲預加載時間,直到頁麵加載完畢
function preloader() {
if (document.images) {
var img1 = new Image();
var img2 = new Image();
var img3 = new Image();
img1.src = "https://domain.tld/path/to/image-001.gif";
img2.src = "https://domain.tld/path/to/image-002.gif";
img3.src = "https://domain.tld/path/to/image-003.gif";
}
}
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);
3.使用Ajax實現預加載
上麵所給出的方法似乎不夠酷,那現在來看一個使用Ajax實現圖片預加載的方法;該方法利用DOM,不僅僅預加載圖片,還會預加載CSS/JavaScript等相關的東西;使用Ajax比直接使用JavaScript優越之處在於JavaScript和CSS的加載不會影響到當前頁麵;該方法簡潔/高效
window.onload = function() {
setTimeout(function() {
// XHR to request a JS and a CSS
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://domain.tld/preload.js');
xhr.send('');
xhr = new XMLHttpRequest();
xhr.open('GET', 'https://domain.tld/preload.css');
xhr.send('');
// preload image
new Image().src = "https://domain.tld/preload.png";
}, 1000);
};
上麵代碼預加載了preload.js/preload.css/preload.png,1000毫秒的超時是為了防止腳本掛起,而導致正常頁麵出現功能問題;下麵我們看看如何用JavaScript來實現該加載過程:
window.onload = function() {
setTimeout(function() {
// reference to <head>
var head = document.getElementsByTagName('head')[0];
// a new CSS
var css = document.createElement('link');
css.type = "text/css";
css.rel = "stylesheet";
css.href = "https://domain.tld/preload.css";
// a new JS
var js = document.createElement("script");
js.type = "text/javascript";
js.src = "https://domain.tld/preload.js";
// preload JS and CSS
head.appendChild(css);
head.appendChild(js);
// preload image
new Image().src = "https://domain.tld/preload.png";
}, 1000);
};
這裏,我們通過DOM創建三個元素來實現三個文件的預加載;正如上麵提到的那樣,使用Ajax加載文件不會應用到加載頁麵上;從這點上看,Ajax方法優越於JavaScript
補充知識
屏幕可視窗口大小
<script>
//原生方法
window.innerHeight || //標準瀏覽器及IE9+
document.documentElement.clientHeight || //標準瀏覽器及低版本IE標準模式
document.body.clientHeight //低版本混雜模式
//jQuery方法
$(window).height();
</script>
滾動條滾動的距離
<script>
//原生方法
window.pagYoffset || //標準瀏覽器及IE9+
document.documentElement.scrollTop || //兼容ie低版本的標準模式
document.body.scrollTop //兼容混雜模式;
//jQuery方法
$(document).scrollTop();
</script>
獲取元素的尺寸
<script>
$(o).width() = o.style.width;
$(o).innerWidth() = o.style.width+o.style.padding;
$(o).outerWidth() = o.offsetWidth = o.style.width+o.style.padding+o.style.border;
$(o).outerWidth(true) = o.style.width+o.style.padding+o.style.border+o.style.margin;
</script>
注意:
要使用原生的style.xxx方法獲取屬性,這個元素必須已經有內嵌的樣式,如<div ></div>;
如果原先是通過外部或內部樣式表定義css樣式,必須使用o.currentStyle[xxx] || document.defaultView.getComputedStyle(0)[xxx]來獲取樣式值
獲取元素的位置信息
<script>
//原生方法
getoffsetTop();
//jQuery方法
$(o).offset().top //元素距離文檔頂的距離
$(o).offset().left //元素距離文檔左邊緣的距離
// 順便提一下返回元素相對於第一個以定位的父元素的偏移距離,注意與上麵偏移距的區別;
jQuery:position() //返回一個對象
$(o).position().left = o.style.left;
$(o).position().top = o.style.top;
</script>
最後更新:2017-11-17 17:34:14