【小技巧】基于vue+swiper實(shí)現(xiàn)圖片庫(kù)列表點(diǎn)擊查看大圖,可左右切換(事件委托、動(dòng)態(tài)數(shù)組、懶加載、緩存、節(jié)流防抖)

最近接到一個(gè)需求,實(shí)現(xiàn)起來(lái)很簡(jiǎn)單,但是考慮到性能問(wèn)題,需要花一些技巧。

頁(yè)面性能優(yōu)化,分別應(yīng)用到:
事件委托、動(dòng)態(tài)數(shù)組、懶加載、緩存、節(jié)流防抖

場(chǎng)景和需求:
移動(dòng)端一個(gè)圖庫(kù)列表頁(yè),幾百?gòu)埳踔翈浊垐D片的列表展示
點(diǎn)擊其中的一張圖片,可以預(yù)覽大圖,左右切換查看所有其他圖片的大圖

- 第一部分:

問(wèn)題:點(diǎn)擊事件,如果每張圖的標(biāo)簽都綁定了事件,在移動(dòng)端,性能很糟糕。

解決辦法:事件委托。

HTML結(jié)構(gòu):

<ul class="photo-items"  @click="previewPhoto" id='photo-list'>
      <li v-for="(item,index) in items" class="photo-item"  :data-index="index">
           <img :src="item.srcUrl"/>
      </li>
</ul>
previewPhoto(ev){
   let oUl = document.getElementById('photo-list');
   let ev = ev || window.event;
   let target = ev.target || ev.srcElement;
   while(target !== oUl ){
        //遞歸調(diào)用,使當(dāng)前點(diǎn)擊對(duì)象指定到li上
        if(target.tagName.toLowerCase() == 'li'){
             const index = parseInt(target.dataset.index)
             break;
        }
       target = target.parentNode;
    }
},

事件委托參考文章:https://www.cnblogs.com/liugang-vip/p/5616484.html

- 第二部分:

問(wèn)題:使用swiper,左右切換圖片,
如果需要查看幾百?gòu)垐D片,就要生成幾百個(gè)swiper-slide,對(duì)性能來(lái)說(shuō)也是一件很糟糕的事情。

如圖:


image.png

解決辦法:使渲染swiper的數(shù)組永遠(yuǎn)只有三張圖片,每次切換,根據(jù)索引讓數(shù)組的三張圖片變一次。

<swiper :options="swiperOption" ref="mySwiper" >
        <swiper-slide  v-for="(item,index) in swiperList" :key="index">
             <img :src="item.srcUrl"/>
        </swiper-slide>
</swiper>
image.png
computed: {
  swiperPicList(){
   if(this.items.length > 0){  //圖片數(shù)組如果不為空
        //curIndex表示當(dāng)前查看的圖片索引
        if(this.curIndex == 0){  //當(dāng)前圖片為第一張時(shí)候,返回的數(shù)組
            return [
               {srcUrl: this.items[this.curIndex].srcUrl},
               {srcUrl: this.items[this.curIndex + 1].srcUrl},
            ] 
        }else if(this.curIndex == (this.items.length - 1)){ //當(dāng)前圖片為最后一張時(shí)候,返回的數(shù)組
             return [
                {srcUrl: this.items[this.curIndex-2].srcUrl},
                {srcUrl: this.items[this.curIndex-1].srcUrl},
                {srcUrl: this.items[this.curIndex].srcUrl},
              ]
         }else{ 
             return [
                {srcUrl: this.items[this.curIndex - 1].srcUrl},
                {srcUrl: this.items[this.curIndex].srcUrl},
                {srcUrl: this.items[this.curIndex + 1].srcUrl},
              ] 
         }
     }
  },
}
image.png

為什么是三張圖?

因?yàn)閟wiper,有兩個(gè)屬性,當(dāng)滑動(dòng)到第一張的時(shí)候isBeginning為true,這可以當(dāng)做用戶向左滑的判斷,當(dāng)滑動(dòng)到最后一張的時(shí)候isEnd為true,同理可以當(dāng)做用戶向右滑動(dòng)的判斷。

[
{srcUrl: this.items[this.curIndex - 1].srcUrl},//當(dāng)前圖片前一張
{srcUrl: this.items[this.curIndex].srcUrl},//當(dāng)前圖片
{srcUrl: this.items[this.curIndex + 1].srcUrl},//當(dāng)前圖片后一張
] 

然后使當(dāng)前索引的圖片永遠(yuǎn)在數(shù)組swiperPicList中間,
1、向左滑動(dòng)的時(shí)候,curIndex--,向右滑動(dòng)的時(shí)候 curIndex++

但是對(duì)于swiper中間選中項(xiàng)向左滑到第一張,第一張會(huì)變成選中項(xiàng),用戶看到的其實(shí)是數(shù)組第一張圖。向右同理。

所以需要使用swiper方法this.swiper.slideTo(1,0, false),使得每次滑動(dòng)之后,swiper都切換到第二個(gè)為選中項(xiàng),這樣用戶看到是就是數(shù)組的中間項(xiàng)。

參考API:
mySwiper.slideTo(index, speed, runCallbacks)
Swiper切換到指定slide。
index:必選,num,指定將要切換到的slide的索引。
speed:可選,num(單位ms),切換速度
runCallbacks: 可選,boolean,設(shè)置為false時(shí)不會(huì)觸發(fā)transition回調(diào)函數(shù)。

除了是第一張和最后一張的時(shí)候做特殊處理。

watch:{
   curIndex(curIndex){
       if(curIndex == 0){ 
         //當(dāng)滑動(dòng)到第一張圖片的時(shí)候,返回的數(shù)組是兩張圖,
         //slideTo的index應(yīng)該為0,跳轉(zhuǎn)到第一張圖。
         this.swiper.slideTo(0,0, false);
       }else if (this.curIndex == (this.items.length-1)){
         //當(dāng)滑動(dòng)到最后一張圖片的時(shí)候,返回?cái)?shù)組是三張圖
         //slideTo的index應(yīng)該為2,跳轉(zhuǎn)到第三張圖。
         this.swiper.slideTo(2,10, false); 
      }else {
         this.swiper.slideTo(1,0, false);
         //其他索引的圖片都是跳到第二張圖
     }
  }
},
computed: {
  swiper() {
    return this.$refs.mySwiper.swiper
  },
  swiperOption(){
    let _this = this  // _this為VUE實(shí)例,要特別注意
    return {
       initialSlide :1,
       on: {
         //滑動(dòng)事件
         //on事件里面的this指向swiper實(shí)例,要特別注意
         transitionEnd: function(){
                //isEnd為true,表示用戶向右滑動(dòng)
                if(this.isEnd){
                   if(_this.curIndex < (_this.items.length-1)){
                        _this.curIndex = _this.curIndex + 1
                      }
                }
               //isBeginning為true,表示用戶向左滑動(dòng)
                if(this.isBeginning){
                   if(_this.curIndex >= 1){
                        _this.curIndex  = _this.curIndex - 1
                   }
                } 
               //這里做的是特殊處理,
              // 因?yàn)楫?dāng)前圖片為最后一張時(shí)候,選中的圖片為第三張,
              //swiperPicList數(shù)組中也是第三張,
              //最后一張滑動(dòng)的方向只有向左,所以_this.curIndex - 1
             //做這個(gè)處理是最后一張向左滑動(dòng)因?yàn)榉祷財(cái)?shù)組的原因,不能用isBeginning來(lái)判斷
               if(_this.curIndex == (_this.items.length-1)){
                     _this.curIndex = _this.curIndex - 1
                }
          },
       }
     }
   },
}

最終效果


image.png

初衷是想讓swiper不需要渲染所有的圖片,臨時(shí)做一個(gè)小數(shù)組,每次切換,小數(shù)組都是動(dòng)態(tài)的獲取相鄰的三張照片,可能不是最優(yōu)的方法,創(chuàng)建數(shù)組也可以進(jìn)行再封裝

以上只是傳達(dá)一個(gè)優(yōu)化思想

swiper參考文檔:https://www.swiper.com.cn/api/methods/109.html

- 第三部分:

問(wèn)題:圖片列表,頁(yè)面上幾百上千張圖片,用戶訪問(wèn)頁(yè)面,要拉很長(zhǎng)時(shí)間,還要考慮用戶在頁(yè)面來(lái)回滾動(dòng)的情況
一、圖片多,要拉很長(zhǎng)時(shí)間

①、懶加載,這個(gè)很簡(jiǎn)單。
②、緩存,這個(gè)也很簡(jiǎn)單。

二、考慮到用戶在頁(yè)面上下來(lái)回滾動(dòng)

③、節(jié)流防抖

節(jié)流:在頻繁觸發(fā)的情況下,按照一定的時(shí)間去執(zhí)行。

//聲明一個(gè)變量當(dāng)標(biāo)志位,記錄當(dāng)前代碼是否在執(zhí)行
const Throttling = (fn,intelval) => {
    let time = null;
        return function (){
            if(!time){
                time = setTimeout(() => {
                    fn.call(this,arguments)
                    time = null
                },intelval) 
            }
     }
}

防抖:在頻繁觸發(fā)的情況下,只有足夠的空閑時(shí)間,才執(zhí)行一次。

//setTimeout做緩存池
const Throttling = (fn,delay) => {
    let time;
    return function (){
        if(time) clearTimeout(time)
        time = setTimeout(() => {
           fn.call(this,arguments)
        },delay) 
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容