最近接到一個(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ō)也是一件很糟糕的事情。
如圖:

解決辦法:使渲染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>

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},
]
}
}
},
}

為什么是三張圖?
因?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
}
},
}
}
},
}
最終效果

初衷是想讓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)
}
}