前端懶加載(也稱為延遲加載或按需加載)是一種網頁性能優化的技術,主要用于在網頁中延遲加載某些資源,如圖片、視頻或其他媒體文件,直到它們實際需要被用戶查看或交互時才進行加載。這種技術特別適用于長頁面或包含大量媒體資源的頁面,因為它可以顯著提高頁面加載速度,減少用戶等待時間,并降低服務器負載。
懶加載的原理為基于視口(viewport)的概念,即用戶當前在屏幕上可見的區域。當頁面加載時,只有視口內的資源會被立即加載,而視口外的資源則會被延遲加載。當用戶滾動頁面或觸發其他交互行為時,視口外的資源才會根據需要被加載。
懶加載的優點:
提高頁面加載速度:由于只加載視口內的資源,頁面初始加載時間大大減少。
節省帶寬和服務器資源:減少不必要的資源加載,降低服務器負載和帶寬消耗。
提升用戶體驗:減少用戶等待時間,使頁面更加流暢和響應迅速。
那么實現懶加載的方式有哪些呢?本文主要從原生、vue、react角度來說一下實現懶加載的常見方法!
loading 屬性指定瀏覽器是應立即加載圖像還是延遲加載圖像。現代瀏覽器已經開始支持img標簽的loading屬性。設置 loading="lazy" 只有鼠標滾動到該圖片所在位置才會顯示。
loading的屬性值有eager和lazy,默認值為eager,表示圖像立即加載;lazy表示圖像延遲加載,只有鼠標滾動到該圖片所在位置才會顯示。
<img src="/images/paris.jpeg" alt="Paris" loading="lazy">
<img src="/images/nature.jpeg" alt="Nature" loading="lazy"
這是實現懶加載最簡單的方法,無需額外的JavaScript代碼。
Intersection Observer API
Intersection Observer API(交叉觀察器 API)提供了一種異步檢測目標元素與祖先元素或頂級文檔的視口相交情況變化的方法。
過去,要檢測一個元素是否可見或者兩個元素是否相交并不容易,很多解決辦法不可靠或性能很差。然而,隨著互聯網的發展,這種需求卻與日俱增,比如,在頁面滾動時“懶加載”圖像或其他內容這些情況都需要用到相交檢測。
const io = new IntersectionObserver(callback, option);
IntersectionObserver是瀏覽器原生提供的構造函數,接受兩個參數:callback是可見性變化時的回調函數,option是配置對象(該參數可選)。
構造函數的返回值是一個觀察器實例。實例的observe方法可以指定觀察哪個 DOM 節點。
// 開始觀察
io.observe(document.getElementById('example'));
// 停止觀察
io.unobserve(element);
// 關閉觀察器
io.disconnect();
上面代碼中,observe的參數是一個 DOM 節點對象。如果要觀察多個節點,就要多次調用這個方法。
io.observe(elementA);
io.observe(elementB);
callack參數:目標元素的可見性變化時,就會調用觀察器的回調函數callback。
一般會觸發兩次:(1)目標元素剛剛進入視口(開始可見);(2)完全離開視口(開始不可見)。
callback函數的參數是一個數組,每個成員都是一個IntersectionObserverEntry對象。
IntersectionObserverEntry 對象:提供目標元素的信息,一共有六個屬性。
time:可見性發生變化的時間,是一個高精度時間戳,單位為毫秒。
target:被觀察的目標元素,是一個 DOM 節點對象。
rootBounds:根元素的矩形區域的信息,getBoundingClientRect()方法的返回值,如果沒有根元素(即直接相對于視口滾動),則返回null。
boundingClientRect:目標元素的矩形區域的信息。
isIntersecting: 布爾值,目標元素與交集觀察者的根節點是否相交(常用)。
intersectionRect:目標元素與視口(或根元素)的交叉區域的信息。
intersectionRatio:目標元素的可見比例,即intersectionRect占boundingClientRect的比例,完全可見時為1,完全不可見時小于等于0。
所以可以通過判斷isIntersecting屬性是否為true來判斷元素的可見性。
對于需要懶加載的圖片,使用data-src代替src。
<img data-src="image.jpg" alt="description">
document.addEventListener("DOMContentLoaded", function() {
const images = document.querySelectorAll('img[data-src]');
const loadImage = function(image) {
image.setAttribute('src', image.getAttribute('data-src'));
image.onload = function() {
image.removeAttribute('data-src');
}
}
//使用IntersectionObserver監聽元素是否進入可視區域
if ('IntersectionObserver' in window) {
var observer = new IntersectionObserver(function(items, observer) {
items.forEach(function(item) {
if (item.isIntersecting) {
loadImage(item.target);
observer.unobserve(item.target);
}
});
}, {
threshold: 0.01
});
images.forEach(function(img) {
observer.observe(img);
});
} else {
//不支持IntersectionObserver,直接加載所有圖片
images.forEach(function(img) {
loadImage(img);
});
}
});
事件監聽+getBoundingClientReact()
舊版本的瀏覽器可能不支持Intersection ObserverAPI,此時可以用 getBoundingClientRect 和事件監聽結合來實現。
Element.getBoundingClientRect() 方法返回一個 DOMRect 對象,其提供了元素的大小及其相對于視口的位置。
返回值是一個 DOMRect 對象,是包含整個元素的最小矩形(包括 padding 和 border-width)。該對象使用 left、top、right、bottom、x、y、width 和 height 這幾個以像素為單位的只讀屬性描述整個矩形的位置和大小。除了 width 和 height 以外的屬性是相對于視圖窗口的左上角來計算的。
const rectObject = object.getBoundingClientRect()
因此,當rectObject.top的值處于0-視口高度,則元素處于可視區。即
getBoundingClientRect(ele).top >= 0 && getBoundingClientRect(ele).top <= offsetHeight
實現如下:
function isElementInviewport(el) {
const rect = el.getBoundingClientRect();
return (
rect.bottom >= 0 &&
rect.right >= 0 &&
rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.left <= (window.innerwidth || document.documentElement.clientWidth);
)
}
function checkLazyLoad() {
const lazyImages = Array.prototype.slice.call(document.querySelectorAll('img[data-src]'));
lazyImages.forEach(function(img) {
if (isElementInViewport(img)) {
img.src = img.getAttribute('data-src');
img.removeAttribute('data-src');
}
});
if (lazyImages.length === 0) { //如果所有懶加載圖片都已加載,移除滾動監聽
window.removeEventListener('scroll', checkLazyLoad);
window.removeEventListener('resize', checkLazyLoad);
}
}
//監聽事件
window.addEventListener('scroll', checkLazyLoad);
window.addEventListener('resize', checkLazyLoad);
window.addEventListener('DOMContentLoaded', checkLazyLoad);
許多第三方庫可以實現懶加載,例如lazysizes 和lozad.js。
以下是使用 lazysizes 庫的示例:
首先,你需要引入lazysizes 庫。
<script src="lazysizes.min.js" async=""></script>
然后修改你的<img>標簽,增加lazyload 類和data-src 屬性來取代 src即可。
<img data-src="image.jpg" class="lazyload" alt="image" />
Iazysizes 庫會自動處理剩下的工作。
在Vue中vue-lazyload插件用的比較多。
首先,安裝vue-lazyload:
npm install vue-lazyload--save
接著在你的Vue項目中引入并使用vue-lazyload:
// main.js
import Vue from 'vue'
import App from './App.vue'
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)
//或者添加選項,例如加載時的占位圖或加載失敗時的圖像
Vue.use(VueLazyload, {
preLoad: 1.3,
error: 'error.png',
loading: 'loading.gif',
attempt: 1
})
new Vue({
render: h => h(App)
}).$mount('#app')
然后在組件中使用v-lazy指令來替換src屬性:
<img v-lazy="imgUrl" alt="image">
React項目中,你可以使用React自帶的lazy函數結合Suspense組件來實現圖片懶加載。
React.lazy目前僅支持默認導出(default exports)如果要加載的組件使用了命名導出,你需要一個中間模塊來重新導出它為默認模塊。
首先,你需要創建一個懶加載組件來包裝圖片:
// LazyImage.jsx
import React from'react';
const LazyImage = ({ src, alt }) => {
return(
<img src={src} alt={alt} />
);
};
export default LazyImage;
然后使用React.lazy來動態導入這個組件:
// App.js 或者其他父組件
import React, { Suspense } from 'react';
const LazyImage = React.lazy(() => import('./LazyImage'));
// 懶加載組件
const App = () =>{
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
{/* 在Suspense組件中渲染懶加載的組件 */}
<LazyImage src="image.jpg" alt="圖片描述"/>
</Suspense>
</div>
);
};
export default App;
Suspense 組件允許你在等待加載時顯示一個加載指示器(例如上面的<div>Loading...</div>)。
注意事項
兼容性:不同的瀏覽器和設備對懶加載的支持程度不同,需要確保所選用的懶加載方案具有良好的兼容性。
用戶體驗:懶加載不應該影響用戶的正常瀏覽和操作。例如,在圖片加載過程中,可以顯示占位符或加載提示,以增加用戶的等待體驗。
SEO優化:雖然懶加載可能會對SEO造成一定影響,但可以通過合理的策略來優化。例如,確保關鍵內容和鏈接在初始加載時即可被搜索引擎索引。
應用場景
長頁面和圖片密集型網站:如新聞網站、社交媒體、電商網站等,這些網站通常包含大量的圖片和長頁面,采用懶加載可以顯著提高頁面性能和用戶體驗。
視頻和音頻資源豐富的網站:如視頻分享網站、音樂平臺等,這些網站的視頻和音頻資源占用大量帶寬和服務器資源,采用懶加載可以有效降低服務器負載和帶寬消耗。
移動設備優化:移動設備的網絡環境通常較差,采用懶加載可以減少用戶的流量消耗和等待時間,提升用戶體驗。
總之,懶加載技術廣泛應用于各類網站和應用中,特別是長頁面、圖片密集的網站、電商網站、新聞網站等。在這些場景中,懶加載技術可以顯著提高頁面性能和用戶體驗。
該文章在 2024/5/28 10:17:07 編輯過