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