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