http請求
如果你學過計算機網絡你就會知道,我們請求一個帶有n張圖片的html文件實際上會發送n+1次請求,因為在瀏覽器解析html的時候遇到了src,就會請求src后面的內容。
設想一下如果我們的頁面有1000000張圖片,那么如果等待這些圖片響應成功並加載完時延是非常大的,而且圖片的加載是同步的,加載時會阻塞瀏覽器繼續向下解析,用戶體驗非常差。
那么我們可不可以讓圖片按需加載呢?當圖片出現在可視區的時候再加載它而不是一開始就加載完全部圖片。
圖片懶加載
template:
<div @scroll="lazyLoad" ref="lazy">
<img v-for="(src, index) in imgs" src="##" :dataSrc="src" :key="index">
<!--more img-->
</div>
改變圖片src
監聽最外層div的滾動事件,觸發滾動時遍歷圖片檢測圖片位置,若在可視區內則顯示
loadImg() { var img = this.$refs.lazy.getElementsByClassName("lazyImg"); // 已滾動高度+可視區高度 var top = this.$refs.lazy.scrollTop + this.$refs.lazy.clientHeight; for(var i = 0; i < img.length; i++) { if(img[i].offsetTop <= top) { // 在可視區內則顯示圖片 img[i].src = img[i].getAttribute("datasrc"); } } }, lazyLoad() { this.loadImg(); }
以上就實現了一個圖片懶加載,本篇文章就到這里,再見。
橋豆麻袋,突然發現一個嚴重的問題:滾動過程中會不斷觸發lazyLoad對圖片做一個遍歷並判斷,那么就會做無數次for循環,更可怕的是,修改一次src會發送一個請求,在滾動的時候我們的for循環每次都從頭判斷並修改src請求圖片,那么請求次數可想而知。
函數防抖
如果在滾動過程中不斷觸發遍歷並判斷圖片是否在可視區的監聽事件,會耗費很大的性能,這里采用函數防抖:當用戶停止滾動時統一遍歷判斷圖片位置
debounce(fn) { // 函數防抖:用戶停止操作之后觸發 clearTimeout(this.timer); this.timer = setTimeout(() => { fn(); }, 1000); }
我們可以將加載圖片的方法放在debounce中
lazyLoad() { this.debounce(this.loadImg); }
這樣當用戶滾動頁面時,松開手才會執行loadImg來遍歷判斷圖片位置。
又出現了一個問題:如果用戶在滾動時從頁面底部上拉到頂部一直沒有松手,那么在這期間都不會執行loadImg,這意味着頁面的圖片都不會顯示,非常影響用戶體驗
防抖優化
我們規定,若用戶上拉高度大於500px那么就自動加載一次可視區內圖片,這里我們用oldScrollTop記錄上次上拉高度
lazyLoad() { // 如果上拉距離大於500px則自動加載 if(this.$refs.lazy.scrollTop - this.oldScrollTop > 500) { this.loadImg(); this.oldScrollTop = this.$refs.lazy.scrollTop; // 更新oldScrollTop } else { // 如果向下拉但小於500px則防抖加載 this.debounce(this.loadImg); } }
下拉優化
當用戶下拉的時候我們並不需要執行lazyLoad,因為我們之前的圖片已經加載過了,所以可以修改一下lazyLoad
lazyLoad() { // 如果上拉距離大於500px則自動加載 if(this.$refs.lazy.scrollTop - this.oldScrollTop > 500) { this.loadImg(); this.oldScrollTop = this.$refs.lazy.scrollTop; } else if(this.$refs.lazy.scrollTop - this.oldScrollTop < 0) { // 如果向下拉則不做操作 return ; } else { // 如果向下拉但小於500px則防抖加載 this.debounce(this.loadImg); } }
減少遍歷個數
最重要的優化已經做完了,但是還可以從一些小細節更加優化一下,我們的loadImg方法中每次都是從0號下標開始遍歷檢查圖片,但是在用戶上拉操作之后一部分圖片已經被加載了,就不需要再次去檢查了。
我們可以用一個變量len記錄上一次被加載后的最后一個圖片,然后修改一下loadImg
loadImg() { var img = this.getImages(); var top = this.$refs.lazy.scrollTop + window.screen.height; // 從len開始檢查 for(var i = this.len; i < img.length; i++) { if(img[i].offsetTop <= top) { img[i].src = img[i].getAttribute("datasrc"); this.len = i; // 更新len } } }
結語
一個完整的優化版圖片懶加載就完成了,我將該功能封裝成了一個vue的插件
源碼:vue-plugins
該插件庫會持續更新,歡迎star & fork~
