Vue圖片懶加載之Vue-Lazyload的使用
一、什么叫懶加載
通俗講 : 懶加載就是延時(shí)加載,即當(dāng)需要用到的時(shí)候再去加載。
那什么叫做需要用到的時(shí)候?比如一個(gè)圖片在沒有出現(xiàn)在可視區(qū)域內(nèi),就已經(jīng)加載當(dāng)頁面里了, 但只有滾動(dòng)頁面下方式才能看見, 則可以認(rèn)為這個(gè)圖片加載的"過早"了。
二、懶加載的優(yōu)點(diǎn)
- 可以減少首頁首次加載的數(shù)量,減少服務(wù)器的壓力
- 當(dāng)網(wǎng)絡(luò)請(qǐng)求比較慢的時(shí)候, 提前給這張圖片添加一個(gè)像素比較低的占位圖片,不至于堆疊在一塊,或顯示大片空白,讓用戶體驗(yàn)更好一點(diǎn)。
三、為什么使用懶加載
可以想象一個(gè)網(wǎng)頁打開有成百上千的圖片需要加載,頁面會(huì)變得非常的卡頓,此時(shí)如果只是可視區(qū)域的圖片加載,其他的圖片可以暫時(shí)有一個(gè)占位 loading 圖,等滾動(dòng)它們到可視區(qū)域時(shí)再去請(qǐng)求真實(shí)圖片并且替換就好了。vue-lazyload 插件就是解決此類問題的,對(duì)vue插件的寫法不熟悉的可以先看一下vue插件
懶加載原理是什么
頁面中的img元素,如果沒有src屬性,瀏覽器就不會(huì)發(fā)出請(qǐng)求去下載圖片,只有通過javascript設(shè)置了圖片路徑,瀏覽器才會(huì)發(fā)送請(qǐng)求。
懶加載的原理就是先在頁面中把所有的圖片統(tǒng)一使用一張占位圖進(jìn)行占位,把真正的路徑存在元素的“data-url”(這個(gè)名字起個(gè)自己認(rèn)識(shí)好記的就行)屬性里,當(dāng)js監(jiān)聽到該圖片元素進(jìn)入可視窗口時(shí),即將自定義屬性中的地址存儲(chǔ)到src屬性中,達(dá)到懶加載的效果。
四、vue中如何實(shí)現(xiàn)懶加載
第一步: 安裝
npm install vue-lazyload --save
第二步: 全局注冊(cè)(main.js)
// main.js 文件 import VueLazyload from 'vue-lazyload' // Vue.use(VueLazyload) //無配置項(xiàng) // 配置項(xiàng) const loadimage = require('assets/img/common/loading.gif') // const errorimage = require('assets/img/common/error.gif') Vue.use(VueLazyload, { preLoad: 1.3, //預(yù)加載的寬高比 loading: loadimage, //圖片加載狀態(tài)下顯示的圖片 // error: errorimage, //圖片加載失敗時(shí)顯示的圖片 attempt: 1, // 加載錯(cuò)誤后最大嘗試次數(shù) }) // img元素上使用v-lazy="src";<img v-lazy="showImage"/>
配置項(xiàng)的參數(shù)說明
鍵 | 描述 | 默認(rèn) | 選項(xiàng) |
---|---|---|---|
preLoad | 表示lazyload的元素, 距離頁面底部距離的百分比. 計(jì)算值為(preload - 1) | 1.3 | Number |
error | 加載失敗后圖片地址 | 'data-src' | String |
loading | 加載時(shí)圖片地址 | 'data-src' | String |
attempt | 圖片加載失敗后的重試次數(shù) | 3 | Number |
listenEvents | 觸發(fā)懶加載的事件 | ['scroll', 'wheel', 'mousewheel', 'resize', 'animationend', 'transitionend', 'touchmove'] | 無 |
adapter | 注冊(cè)img 的loading,loaded,error 三個(gè)狀態(tài)的回調(diào)函數(shù), 參數(shù)會(huì)暴露懶加載的img元素, 可以對(duì)其進(jìn)行操作. | { } | 無 |
filter | img未加載之前, 解析到src 的時(shí)候注冊(cè)的回調(diào)函數(shù). 可以在加載圖片之前,對(duì)src進(jìn)行修改. 注冊(cè)在filter下的所有的函數(shù)都會(huì)執(zhí)行 | { } | 無 |
lazyComponent | 是否啟用懶加載組件. <lazy-component>組件中的內(nèi)容 只有在出現(xiàn)在preload的 位置中才會(huì)加載組件. 這個(gè)lazyloadComponent 組件有個(gè)缺點(diǎn) 就是,組件在加載前 是什么都不渲染的, 這樣子的話,有可能會(huì)影響布局, 以及加載前到加載后的切換不好, 有點(diǎn)突兀和生硬. | false | 無 |
dispatchEvent | 觸發(fā)dom事件 | false | Boolean |
throttleWait | 等待時(shí)長(zhǎng) | 200 | Number |
observer | 是否啟用IntersectionObserver, 這個(gè)api有兼容問題 | false | Boolean |
observerOptions | IntersectionObserver選項(xiàng) | { rootMargin: '0px', threshold: 0.1 } | 無 |
silent | 不打印調(diào)試信息 | true | Boolean |
因?yàn)閟rc中的文件會(huì)被webpack編譯,assets文件夾中的圖片地址,會(huì)在編譯過程中重命名。vue-lazyload是在main.js文件中引入,不會(huì)被webpack進(jìn)行編譯,因此vue-lazyload無法獲得正確的圖片地址,所以直接寫相對(duì)地址就無法獲取到圖片正確地址
第三步: 寫loading圖片的樣式(不是必須, 視情況而定)
img[lazy="loading"]{ display:block; width:50px !important; height:50px !important; margin:0 auto; }
第四步: 使用 ( :src--->v-lazy )
<div class="lazyLoad"> <ul> <li v-for="img in arr"> <img v-lazy="img.thumbnail_pic_s"> </li> </ul> </div>
這里有個(gè)坑需要注意如設(shè)置了翻頁功能,且每一頁都是請(qǐng)求的數(shù)據(jù)進(jìn)行渲染。
會(huì)發(fā)現(xiàn)其他的數(shù)據(jù)都變了,唯獨(dú)圖片還是原來的圖片。
由于使用的數(shù)據(jù)是父組件傳過來的,第一個(gè)想到父組件axios異步請(qǐng)求的數(shù)據(jù)導(dǎo)致子組件可能數(shù)據(jù)沒有動(dòng)態(tài)更新。但監(jiān)聽了下數(shù)據(jù),發(fā)現(xiàn)確實(shí)是改變了 .
解決辦法只要加個(gè)key就行, 如下代碼
<ul> <li v-for="img in list"> <img v-lazy="img.src" :key="img.src" > </li> </ul>
五、js---懶加載的實(shí)現(xiàn)步驟?
1)首先,不要將圖片地址放到src屬性中,而是放到其它屬性(data-original)中。
2)頁面加載完成后,根據(jù)scrollTop判斷圖片是否在用戶的視野內(nèi),如果在,則將data-original屬性中的值取出存放到src屬性中。
3)在滾動(dòng)事件中重復(fù)判斷圖片是否進(jìn)入視野,如果進(jìn)入,則將data-original屬性中的值取出存放到src屬性中。
懶加載代碼實(shí)現(xiàn)
方式一:原生js
元素距頂部的高度 - 頁面被卷去的高度 <= 瀏覽器可視區(qū)的高度)
來判斷是否符合我們想要的條件.需要實(shí)時(shí)監(jiān)聽頁面滾動(dòng)時(shí) 圖片的高度變化
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> * { padding: 0; margin: 0; list-style: none; } img { width: 400px; height: 300px; } </style> <script> window.onload = function () { var imgs = document.querySelectorAll("img"); // 初始化執(zhí)行 lazyLoad(imgs); // 滾動(dòng)執(zhí)行 window.addEventListener("scroll", function () { lazyLoad(imgs); }); function lazyLoad(imgs) { for (let i = 0; i < imgs.length; i++) { var imgoffsetT = imgs[i].offsetTop; // 圖片的距頂部的高度 var wheight = window.innerHeight; // 瀏覽器可視區(qū)的高度 var scrollT = document.documentElement.scrollTop; // 頁面被卷去的高度 if (imgoffsetT - scrollT <= wheight) { // 判斷圖片是否將要出現(xiàn) imgs[i].src = imgs[i].dataset.src; // 出現(xiàn)后將自定義地址轉(zhuǎn)為真實(shí)地址 } } } }; /* obj.getAttribute("屬性名") 通過元素節(jié)點(diǎn)的屬性名稱獲取屬性的值。 使用data-前綴設(shè)置我們需要的自定義屬性,來進(jìn)行一些數(shù)據(jù)的存放, dataset 獲取自定義屬性值的使用 */ </script> </head> <body> <ul> <li> <img data-src="./img/img1.gif" src="./img/loading.gif" alt="" /> </li> <li> <img data-src="./img/img2.gif" src="./img/loading.gif" alt="" /> </li> <li> <img data-src="./img/img3.gif" src="./img/loading.gif" alt="" /> </li> <li> <img data-src="./img/img4.gif" src="./img/loading.gif" alt="" /> </li> <li> <img data-src="./img/img5.png" src="./img/loading.gif" alt="" /> </li> </ul> </body> </html>
方式二: getBoundingClientRect()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> * { padding: 0; margin: 0; list-style: none; } img { width: 400px; height: 300px; } </style> <script> window.onload = function () { var imgs = document.querySelectorAll("img"); // 初始調(diào)動(dòng)一次 lazyLoad(); window.addEventListener("scroll", throttle(lazyLoad, 1000), false); //函數(shù)1:封裝判定圖片是否在可視區(qū) function isInVisibleArea(imgOne) { const info = imgOne.getBoundingClientRect(); // 獲取頁面可視區(qū)的高度,寬度 let windowH = window.innerHeight; let windowW = window.innerWidth; // 限定參數(shù)在可視區(qū)內(nèi) let res = info.bottom > 0 && info.top < windowH && info.right > 0 && info.left < windowW; return res; } //函數(shù)2: 封裝滾動(dòng)時(shí)重新加載函數(shù) function lazyLoad() { for (let i = 0; i < imgs.length; i++) { const imgOne = imgs[i]; // 判定是否在可視區(qū)內(nèi) if (isInVisibleArea(imgOne)) { // 替換src方法一: // imgOne.src = imgOne.getAttribute("data-src"); // 替換src方法二: imgOne.src = imgOne.dataset.src; // imgs.splice(i,1) // i--; } console.log("我滾了"); //所以要做節(jié)流操作 } } //函數(shù)3:節(jié)流函數(shù) /* 參數(shù)1:函數(shù) 參數(shù)2:執(zhí)行時(shí)間 */ function throttle(fn, time = 250) { let lastTime = null; return function (...args) { const now = Date.now(); //當(dāng)前時(shí)間 if (now - lastTime >= time) { fn();//幫助執(zhí)行函數(shù),改變上下文 lastTime = now; } }; } }; /* getBoundingClientRect() ——獲取元素位置,這個(gè)方法沒有參數(shù) ——用于獲得頁面中某個(gè)元素的左,上,右和下分別相對(duì)瀏覽器視窗的位置。 ——是DOM元素到瀏覽器可視范圍的距離(不包含文檔卷起的部分)。 該函數(shù)返回一個(gè)Object對(duì)象,該對(duì)象有6個(gè)屬性:top,lef,right,bottom,width,height; */ </script> </head> <body> <ul> <li> <img data-src="./img/img1.gif" src="./img/loading.gif" alt="" /> </li> <li> <img data-src="./img/img2.gif" src="./img/loading.gif" alt="" /> </li> <li> <img data-src="./img/img3.gif" src="./img/loading.gif" alt="" /> </li> <li> <img data-src="./img/img4.gif" src="./img/loading.gif" alt="" /> </li> <li> <img data-src="./img/img5.png" src="./img/loading.gif" alt="" /> </li> </ul> </body> </html>
上面的方法需要頻繁觸發(fā) scroll 事件,很容易造成卡頓或者頁面性能問題。
方式三:IntersectionObserver(callback)
1.概念
IntersectionObserve 是瀏覽器提供的一個(gè)原生構(gòu)造函數(shù),它也被稱作交叉觀察器。 它可以觀察我們的元素是否可見,也就是是否和可視區(qū)發(fā)生交叉。官網(wǎng)的解釋:IntersectionObserver 接口提供了一種異步觀察目標(biāo)元素與其祖先元素或頂級(jí)文檔視窗(viewport)交叉狀態(tài)的方法。祖先元素與視窗(viewport)被稱為根(root)。
官網(wǎng)說的稍微晦澀一點(diǎn),我們通俗的給大家解釋一下,結(jié)合一張圖應(yīng)該就很好里面了。
通俗的解釋:我們可以使用 IntersectionObserver 接口觀察一個(gè)元素,觀察它是否進(jìn)入了可視區(qū),這個(gè)可視區(qū)可以相對(duì)于視窗或者祖先元素。
上面的圖就很形象的描述了一個(gè)元素逐步出現(xiàn)在可視區(qū)內(nèi)的過程,當(dāng)元素和可視區(qū)發(fā)生交叉時(shí),則代表進(jìn)入可視區(qū)內(nèi)了。而我們的 “交叉觀察器” IntersectionObserve 就和名字一樣,專門用來觀察何時(shí)交叉。
2.基本使用
IntersectionObserve 使用起來很簡(jiǎn)單,我們了解了它接收的參數(shù)以及攜帶的方法如何使用后,便可以很快的上手。
2.1 初始化實(shí)例
因?yàn)樗且粋€(gè)構(gòu)造函數(shù),所以我們可以使用 new 的方式實(shí)例化它,代碼如下:
<script> let IO = new IntersectionObserver(callback, options); </script>
該構(gòu)造函數(shù)接收兩個(gè)參數(shù):
- callback:回調(diào)函數(shù),當(dāng)元素的可見性發(fā)生變化,即元素與目標(biāo)元素相交發(fā)生改變時(shí)會(huì)觸發(fā)該回調(diào)函數(shù)。
- options:一些配置項(xiàng)參數(shù),如果不傳會(huì)有默認(rèn)值,它可以用來配置可視區(qū)元素、什么時(shí)候觸發(fā)回調(diào)等等,默認(rèn)就是瀏覽器視口。
2.2 回調(diào)函數(shù)參數(shù)callback 會(huì)接收兩個(gè)參數(shù)
主要解釋如下:
entries:它是一個(gè) IntersectionObserverEntry 對(duì)象數(shù)組 ,IntersectionObserverEntry 主要存儲(chǔ)的是一些觀察元素的信息,主要有以下 7 個(gè)屬性:
- time:可見性發(fā)生變化的時(shí)間,是一個(gè)高精度時(shí)間戳,單位為毫秒
- target:被觀察的目標(biāo)元素,是一個(gè) DOM 節(jié)點(diǎn)對(duì)象
- rootBounds:根元素的矩形區(qū)域的信息,getBoundingClientRect()方法的返回值,如果沒有根元素(即直接相對(duì)于視口滾動(dòng)),則返回 null
- boundingClientRect:目標(biāo)元素的矩形區(qū)域的信息
- isIntersecting:目標(biāo)元素當(dāng)前是否可見 Boolean 值 可見為 true
- intersectionRect:目標(biāo)元素與視口(或根元素)的交叉區(qū)域的信息
- intersectionRatio:目標(biāo)元素的可見比例,即 intersectionRect 占 boundingClientRect 的比例,完全可見時(shí)為 1,完全不可見時(shí)小于等于 0
observer:它返回的是被調(diào)用的 IntersectionObserve 實(shí)例,我們通常無需操作。
2.3 options 配置options 是構(gòu)造函數(shù)的第二個(gè)參數(shù),是一個(gè)對(duì)象的形式,它主要一些配置信息,主要配置項(xiàng)有如下幾個(gè):
- root:主要用來配置被觀察元素是相對(duì)于誰可見和不可見,如果不配置,則默認(rèn)的是瀏覽器視口。
- threshold:主要用來配置兩個(gè)元素的交叉比例,它是一個(gè)數(shù)組,用于決定在什么時(shí)候觸發(fā)回調(diào)函數(shù)。
- rootMargin:用來改變可視區(qū)域的范圍,假如我們可視區(qū)域大小是 300x300,可以通過該參數(shù)改變可視區(qū)域大小,但是實(shí)際像素值并沒有變,優(yōu)點(diǎn)類似于我們上拉加載更多場(chǎng)景:當(dāng)距離底部多少多少像素的時(shí)候就加載。
看圖理解:
let viewport = document.getElementById("viewport"); // 可視區(qū)域 let options = { root: viewport, threshold: [0, 0.5, 1], rootMargin: '30px 100px 20px' }
2.4 實(shí)例方法
初始化實(shí)例后,我們就可以調(diào)用實(shí)例方法了。IntersectionObserver 實(shí)例常用的方法常主要有下面幾個(gè):
- IO.observe([element]):使用該方法后代表我們開始觀察某個(gè)元素了,它接收一個(gè)元素節(jié)點(diǎn)作為參數(shù),也就是被觀察元素。
- IO.unobserve([element]):該方法用于停止觀察某元素,同樣接收一個(gè)元素節(jié)點(diǎn)作為參數(shù)。
- IO.disconnect():該方法用于關(guān)閉觀察器。
可以先簡(jiǎn)單演示一下,看看何時(shí)觸發(fā) callback。
3.代碼演示
3.1 查看 entries 和 observe
我們先來看一下回調(diào)函數(shù)里面默認(rèn)傳遞的參數(shù)打印出來是什么:entries 和 observe。
示例代碼:
<head> <style> .viewport { width: 300px; height: 200px; border: 1px solid blue; overflow: auto; } .box1 { height: 600px; width: 100%; } .observed { width: 100px; height: 100px; border: 1px solid green; } </style> </head> <body> <div class="viewport" id="viewport"> <div class="box1"> <div class="observed" id="observed"></div> </div> </div> </body> <script> let viewport = document.getElementById("viewport"); // 可視區(qū)域 let observed = document.getElementById("observed"); // 被觀察元素 let options = { root: viewport, // 指定可視區(qū)元素 } let IO = new IntersectionObserver(IOCallback, options); // 初始化實(shí)例 IO.observe(observed); // 開始觀察 // 回調(diào)函數(shù) function IOCallback(entries, observer) { console.info("entries", entries); console.info("observer", observer); } </script>
輸出結(jié)果:
這里的代碼還比較簡(jiǎn)單,我們這里設(shè)置了視圖窗口為我們指定的 id 為 viewport 的元素,被觀察元素為 id 為 observed 的元素。當(dāng)我們刷新頁面的時(shí)候,IOCallback 回調(diào)函數(shù)便會(huì)執(zhí)行,且打印了 entries 和 observe,至于它們中每個(gè)參數(shù)代表的意義大家可以參照上一節(jié)。
3.2 實(shí)現(xiàn)圖片懶加載
圖片懶加載是我們非常常見的一個(gè)場(chǎng)景了,這里我們拿這個(gè)場(chǎng)景距離相信大家可以更加容易理解。
需求背景:我們有非常多的圖片,如果一次性全部渲染,非常消耗性能。所以我們需要實(shí)現(xiàn)圖片出現(xiàn)在可視區(qū)域內(nèi)后在進(jìn)行渲染加載。
實(shí)現(xiàn)思路:
- 先確定可視區(qū)窗口
- 為所有 img 標(biāo)簽添加一個(gè)自定義 data-src 屬性,用來存放圖片真正路徑
- 利用 IntersectionObserve 觀察每一張圖片是否進(jìn)入可視區(qū)內(nèi)
- 如果進(jìn)入可視區(qū)內(nèi),則將圖片的 src 路徑替換為真正的 data-src 路徑
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> * { padding: 0; margin: 0; list-style: none; } img { width: 400px; height: 300px; } </style> </head> <body> <ul id="view"> <li> <img data-src="./img/img1.gif" src="./img/loading.gif" alt="" /> </li> <li> <img data-src="./img/img2.gif" src="./img/loading.gif" alt="" /> </li> <li> <img data-src="./img/img3.gif" src="./img/loading.gif" alt="" /> </li> <li> <img data-src="./img/img4.gif" src="./img/loading.gif" alt="" /> </li> <li> <img data-src="./img/img5.png" src="./img/loading.gif" alt="" /> </li> </ul> <script> const imgs = document.querySelectorAll("img"); const callback = (res) => { //res 是觀察的元素?cái)?shù)組 info 每個(gè)被觀察的圖片信息 // 循環(huán)所有觀察元素 res.forEach((info) => { // isIntersecting 目標(biāo)是否被觀察到,返回布爾值 if (info.isIntersecting) { // img 就是當(dāng)前的圖片標(biāo)簽 const img = info.target; img.src = img.getAttribute("data-src"); // 真實(shí)地址替換后 取消對(duì)它的觀察 obs.unobserve(img); console.log("觸發(fā)"); } }); }; const obs = new IntersectionObserver(callback);// 實(shí)例化 IntersectionObserver // 遍歷imgs所有的圖片,然后給每個(gè)圖片添加觀察實(shí)例 imgs.forEach((img) => { // observe : 被調(diào)用的IntersectionObserver實(shí)例。給每個(gè)圖片添加觀察實(shí)例 obs.observe(img); }); /* IntersectionObserver(callback) callback回調(diào)觸發(fā)兩次,看見了出發(fā),看不見也觸發(fā) */ </script> </body> </html>
警告:IE 不兼容
到此這篇關(guān)于Vue 圖片懶加載 之 Vue-Lazyload的使用的文章就介紹到這了,更多相關(guān)Vue-Lazyload 使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue + el-form 實(shí)現(xiàn)的多層循環(huán)表單驗(yàn)證
這篇文章主要介紹了vue + el-form 實(shí)現(xiàn)的多層循環(huán)表單驗(yàn)證,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下。2020-11-11Vue實(shí)現(xiàn)簡(jiǎn)易翻頁效果源碼分享
本文給大家分享了vue實(shí)現(xiàn)簡(jiǎn)易翻頁效果,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-11-11關(guān)于Vue背景圖打包之后訪問路徑錯(cuò)誤問題的解決
本篇文章主要介紹了關(guān)于Vue背景圖打包之后訪問路徑錯(cuò)誤問題的解決,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11Vue中video標(biāo)簽如何實(shí)現(xiàn)不靜音自動(dòng)播放
最近在做大屏展示需要在一開始播放引導(dǎo)視頻,產(chǎn)生自動(dòng)播放需求,下面這篇文章主要給大家介紹了關(guān)于Vue中video標(biāo)簽如何實(shí)現(xiàn)不靜音自動(dòng)播放的相關(guān)資料,需要的朋友可以參考下2023-01-01vue3中的對(duì)象時(shí)為proxy對(duì)象如何獲取值(兩種方式)
使用vue3.0時(shí),因?yàn)榈讓邮鞘褂胮roxy進(jìn)行代理的所以當(dāng)我們打印一些值得時(shí)候是proxy代理之后的是Proxy<BR>對(duì)象,Proxy對(duì)象里邊的[[Target]]才是真實(shí)的對(duì)象,那么如何獲取這個(gè)值呢,下面下面給大家介紹兩種方式,感興趣的朋友一起看看吧2023-01-01使用vue-cli創(chuàng)建vue項(xiàng)目介紹
這篇文章介紹了使用vue-cli創(chuàng)建vue項(xiàng)目的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-01-01