iframe懒加载_懒加载是如何实现的?
作為網頁內容的一部分,圖像和視頻通常要消耗很多資源加載。要提高網頁應用的性能,如何避免資源浪費在加載圖像和視頻上就很重要了。但是,很多時候我們都不愿意減少網頁上的媒體資源,所以我們經常無從下手。幸運的是,我們有懶加載這個絕招,它可以幫助我們減少加載時間和降低負載,而不在內容上偷工減料。
什么是懶加載?
懶加載是一種在頁面加載時延遲加載一些非關鍵資源的技術,換句話說就是按需加載。對于圖片來說,非關鍵通常意味著離屏。如果你有使用過Lighthouse并且做過一些性能調優,你可能已經見過一些離屏圖片的應用。(offscreen-images)
我們之前看到的懶加載一般是這樣的形式:
瀏覽一個網頁,準備往下拖動滾動條
拖動一個占位圖片到視窗
占位圖片被瞬間替換成最終的圖片
我們可以在Medium中看到懶加載是如何使用的,網頁首先用一張輕量級的圖片占位,當占位圖片被拖動到視窗,瞬間加載目標圖片,然后替換占位圖片。
如果你不是很熟悉懶加載技術,你或許會疑問它有什么用,能為我們帶來什么好處,下面我們將會探討這個問題。
為什么要懶加載而不直接加載?
浪費流量。在不計流量收費的網絡,這可能不重要;在按流量收費的網絡中,毫無疑問,一次性加載大量圖片就是在浪費用戶的錢。
消耗額外的電量和其他的系統資源,并且延長了瀏覽器解析的時間。因為媒體資源在被下載完成后,瀏覽器必須對它進行解碼,然后渲染在視窗上,這些操作都需要一定的時間。
懶加載圖片和視頻,可以減少頁面加載的時間、頁面的大小和降低系統資源的占用,這些對于性能都有顯著地提升。在這里,我們將會提到一些懶加載技術和使用方法,還有一些常用的懶加載庫。
懶加載圖片
圖片懶加載在技術上實現很簡單,不過對于細節要求比較嚴格。目前有很多實現懶加載的方法,先從懶加載內聯圖片說起吧。
內聯圖片
最常見的懶加載方式就是利用標簽。懶加載圖片時,我們利用JavaScript檢查標簽是否在視窗中。如果在,的src(有時候是srcset)就會設置為目標圖片的url。
利用intersection observer
如果你之前用過懶加載,你很可能是通過監聽一些事件比如scroll或者resize來檢測元素出現在視窗,這種方法很成熟,能夠兼容大部分的瀏覽器。但是,現代瀏覽器提供了一個更好的方法給我們(the intersection observer API)
注意:Intersection observer目前只能在Chrome63+和firefox58+使用
比起事件監聽,Intersection observer用起來比較簡單,可閱讀性也大大提高。開發者只需要注冊一個observer去監控元素而不是寫一大堆亂七八糟的視窗檢測代碼。注冊observer之后我們只需要做的就是當元素可見時改變它的行為。舉個例子吧:
需要注意三個相關的屬性
class:用于在JavaScript中關聯元素
src屬性:指向了一張占位圖片,圖片在頁面的第一次加載會顯現
data-src和data-srcset屬性:這是占位屬性,里面放的是目標圖片的url
ok,看一下怎么在JavaScript中使用Intersection observer吧:
document.addEventListener("DOMContentLoaded", function() {
var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove("lazy");
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
} else {
// Possibly fall back to a more compatible method here
}
});
當DOMContentLoaded觸發后,js會查詢class為lazy的img元素。然后我們檢測瀏覽器支不支持intersection observer,如果可以用,先創建一個observer,然后傳入回調函數,回調函數將會在元素可見性變化時被調用。具體的代碼可以在這里查看。
最后比較麻煩的是處理兼容性,在不支持intersection observer的瀏覽器,你需要引入polyfill,或者回退到更安全的方法。
利用事件
當你選擇使用intersection observer來實現懶加載時,你要考慮它的兼容性,當然你可以使用polyfill,實際上這也非常簡單。事實上你也可以針對低版本的瀏覽器使用事件來完成更安全地回退。你可以使用scroll、resize和orientationchange事件,再配合getBoundingClientRectAPI就可以實現懶加載了。
和上面一樣的例子,現在JavaScript程序變成了這樣:
document.addEventListener("DOMContentLoaded", function() {
let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
let active = false;
const lazyLoad = function() {
if (active === false) {
active = true;
setTimeout(function() {
lazyImages.forEach(function(lazyImage) {
if ((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display !== "none") {
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove("lazy");
lazyImages = lazyImages.filter(function(image) {
return image !== lazyImage;
});
if (lazyImages.length === 0) {
document.removeEventListener("scroll", lazyLoad);
window.removeEventListener("resize", lazyLoad);
window.removeEventListener("orientationchange", lazyLoad);
}
}
});
active = false;
}, 200);
}
};
document.addEventListener("scroll", lazyLoad);
window.addEventListener("resize", lazyLoad);
window.addEventListener("orientationchange", lazyLoad);
});
上面的代碼用了getBoundingClientRect,在scroll事件中檢測img是否在視窗。setTimeout用于延遲執行操作,active變量代表了處理狀態防止同時響應。當圖片被懶加載完成后,事件處理程序將被移除,具體請看這里。
盡管上面這段代碼可以在絕大部分的瀏覽器上運行,但存在顯著的性能損耗。在此示例中,無論在視口中是否存在圖像,文檔滾動或窗口大小調整時都會每200毫秒執行一次檢查。 另外,跟蹤有多少元素留給延遲加載和解除事件處理程序的繁瑣工作也留給了開發者。
建議:盡可能使用intersection observer,如果應用要求兼容低版本的瀏覽器才考慮利用事件
CSS圖像
展示圖像不是標簽的特權,CSS利用background-image也可以做到。相比較而言,CSS加載圖片比較容易控制。當文檔對象模型、CSS對象模型和渲染樹被構造完成后,開始請求外部資源之前,瀏覽器會檢測CSS規則是怎么應用到DOM上的。如果瀏覽器檢測到CSS引用的外部資源并沒有應用到已存在的DOM節點上,瀏覽器就不會請求這些資源。
這個行為可用于延遲CSS圖片資源的加載,思路是通過JavaScript檢測到元素處于視窗中時,加一個class類名,這個class就引用了外部圖片資源。
這可以實現圖片按需加載而不是一次性全部加載。給個例子:
Here's a hero heading to get your attention!
Here's hero copy to convince you to buy a thing!
Buy a thing!
這個div.lazy-background元素會正常地顯示CSS規則加載的占位圖片。當元素處于可見狀態時,我們可以添加一個類名完成懶加載:
.lazy-background {
background-image: url("hero-placeholder.jpg"); /* 占位圖片 */
}
.lazy-background.visible {
background-image: url("hero.jpg"); /* 真正的圖片 */
}
下面是利用JavaScript去檢測元素是否處于視窗(intersection observer),如果可見就為它加上一個visible的類名。
document.addEventListener("DOMContentLoaded", function() {
var lazyBackgrounds = [].slice.call(document.querySelectorAll(".lazy-background"));
if ("IntersectionObserver" in window) {
let lazyBackgroundObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
lazyBackgroundObserver.unobserve(entry.target);
}
});
});
lazyBackgrounds.forEach(function(lazyBackground) {
lazyBackgroundObserver.observe(lazyBackground);
});
}
});
懶加載視頻
就像圖片一樣,我們同樣可以懶加載視頻,播放視頻會用到標簽。如何懶加載視頻取決于特定的場景,先來討論幾個需要不同解決方案的場景。
視頻不需要自動播放
我們還需要添加一個poster屬性給標簽,這相當于一個占位符。preload屬性則規定是否在頁面加載后載入視頻。鑒于瀏覽器之間的preload默認值差異,顯式定義會更具兼容性。在這種情況下,當用戶點擊播放視頻時,視頻才會被加載,預加載視頻簡單地實現了。不幸的是,當我們想用視頻替代GIF動畫時,這個方法就行不通了。
用視頻模擬GIF
GIF在很多地方都不及視頻,特別是文件大小方面。在相同質量下,視頻的尺寸通常會比GIF文件小得多。當然,利用視頻取代GIF并不是直接用標簽取代標簽那么簡單。因為GIF圖片有三種要注意的行為:
加載完后自動播放
不停地循環播放
沒有音軌
要實現這些,HTML是這樣的:
autoplay、muted和loop的作用是為了實現上述三個功能,playsinline是為了兼容IOS的autoplay?,F在我們已經有了一個跨平臺的視頻模版用于取代GIF圖片了。接下來怎么進行懶加載呢?Chrome會幫我們自動完成這項工作,但你不能保證所有瀏覽器都能做到這個。所以,還是動手實現一下吧。首先,修改標簽的屬性:
注意到了嗎?有一個奇怪的poster屬性。這個屬性其實是一個占位符,在被懶加載之前,poster里面指定的內容會在標簽中顯現。和之前的圖片懶加載一樣,我們指定真正的video url藏于每個的data-src中。下一步,JavaScript程序該出場了:
document.addEventListener("DOMContentLoaded", function() {
var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy"));
if ("IntersectionObserver" in window) {
var lazyVideoObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(video) {
if (video.isIntersecting) {
for (var source in video.target.children) {
var videoSource = video.target.children[source];
if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") {
videoSource.src = videoSource.dataset.src;
}
}
video.target.load();
video.target.classList.remove("lazy");
lazyVideoObserver.unobserve(video.target);
}
});
});
lazyVideos.forEach(function(lazyVideo) {
lazyVideoObserver.observe(lazyVideo);
});
}
});
當懶加載一個視頻的時,首先要迭代標簽里面的每一個,然后將data-src中的url分配給src屬性。然后調用元素的load方法,現在視頻就可以自動播放了。
通過這個方法,我們有了一個模擬GIF動畫的視頻解決方案,不會消耗帶寬加載不必要的媒體資源,而且還能實現懶加載。
懶加載庫
如果你不關心懶加載背后是如何實現的,你只是想找一個庫去實現這個功能,可供選擇的有:
lazysizes 是一個功能十分強大的懶加載庫,主要用于加載圖片和iframes。你只需要指定data-src/data-srcset屬性,lazysizes會幫你自動懶加載內容。值得注意的是,lazysizes基于intersection observer,因此你需要一個polyfill。你還可以通過一些插件擴展庫的功能以用于懶加載視頻。
lozad.js是一個輕量級、高性能的懶加載庫,基于intersection observer,你同樣需要提供一個相關的polyfill。
blazy是一個輕量級的懶加載庫,大小僅為1.4KB。相對于lazysizes,它不需要任何的外部依賴,并且兼容IE7+。你可能猜測到了,blazy不支持intersection observer,性能相對較差。
yall.js是作者本人寫的一個懶加載庫,基于IntersectionObserver和事件,兼容IE11和大部分的瀏覽器。
如果你想尋找一個基于React的懶加載工具,react-lazyload可能是你的選擇。
上述每個懶加載庫的文檔都寫得很好,同時提供了大量的標記模式。如果你不想深究懶加載的技術細節,就選擇任意一個去使用,這能節省你很多的時間和功夫。
容易出錯的地方
看到有那么多的庫可以實現懶加載,你可能會以為這是一項很輕松的工作。但是,懶加載一旦出現錯誤,會導致意想不到的后果。為了避免出錯,下面的建議你最好熟讀于心:
布局偏移和占位符
如果你沒有使用占位符,懶加載會導致頁面布局的偏移。除了讓用戶感到困惑之外,還會導致不必要的瀏覽器reflow,性能大幅下降。因此,你至少也要使用一張固定的圖片填充img標簽,或者使用像LQIP和SQIP這樣的技術在加載之前提示媒體資源的內容。
對于標簽,src屬性初始化應該指向一張占位圖片,最終會被替換成目標圖片。對于,可以使用poster屬性指定占位符。除此之外,對于和來說,顯式聲明其width和height屬性都是十分必要的,這可以保證從占位符切換到目標資源的過程中不會導致瀏覽器reflow。
延遲圖片解碼
用JavaScript加載一些比較大的圖片會阻塞線程,導致網頁短暫地失去交互能力。如果你不樂意這樣的情況出現,用decode方法異步解碼圖片是一個很好的選擇,這可以避免阻塞線程,下面展示一下例子:
var newImage = new Image();
newImage.src = "my-awesome-image.jpg";
if ("decode" in newImage) {
// Fancy decoding logic
newImage.decode().then(function() {
imageContainer.appendChild(newImage);
});
} else {
// Regular image load
imageContainer.appendChild(newImage);
}
上面的代碼主要是用了Image.decode()方法,具體的請看清這里(this CodePen link)。如果你的圖片不是太大,可以選擇直接同步加載。
加載失敗
有時候媒體資源會加載失敗,假象有這種情況:一個網頁設置了HTML短時間的緩存(大概5分鐘),然后用戶打開了一個tab幾小時后再瀏覽這個網頁。在這個過程的某個時間,緩存會重新部署,基于hash的版本號會改變或者丟失。如果這時候懶加載圖片,會導致失敗。
雖然這種情況有點極端,但你必須要有策略來應對圖片加載失敗這種狀況。比如你可以用一個按鈕代替圖片填充,用戶可以點擊這個按鈕重新加載圖片,或者提示用戶發生了錯誤。
無論多小概率的錯誤可能會出現,所以說不管怎樣,及時提示用戶網頁加載錯誤或者提供一個重新加載的按鈕總是值得的。
總結
懶加載可以減少頁面加載的時間、降低頁面負載。在用戶瀏覽的時候,網頁不會加載那些用戶看不到的內容,但如果用戶愿意,用戶依然可以正常地瀏覽這些內容。
就改進性能方面,懶加載是無可爭議的,合理的一項技術。如果網頁應用中出現了大量的圖片,懶加載可以完美地限制不必要的加載,對于用戶體驗,這是巨大的提升,相信我,你的用戶和老板會感謝你的。
水平有限,翻譯得不好的地方請見諒,看到錯誤請在留言反饋,謝謝
總結
以上是生活随笔為你收集整理的iframe懒加载_懒加载是如何实现的?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JAVA入门级教学之(参数传递)
- 下一篇: 计算机网络中缓存技术,编程达人