记录原生小程序swiper渲染过多导致的页面卡顿问题解决思路

记录原生小程序swiper渲染过多导致的页面卡顿问题解决思路

本文稍有啰嗦,主要是记录下思路,啰嗦就啰嗦点吧哈哈

遇到的问题

最近在开发小程序的过程中遇到一个问题,在苹果手机上,小程序的首页在首次加载完后会出现三四秒的卡顿,此时任何的页面交互都卡卡的,操作不顺畅,过去个三四秒后才恢复正常。安卓手机上确很流畅,可能安卓的手机性能超越了苹果吧。但我的手机是苹果,对于经常使用各种应用的我来说,页面加载完过三四秒才能操作,是绝对不能忍的,何况是用户...

思路

首页的接口确实挺多的,一开始以为是接口太多,数据太多造成的卡顿,试着去屏蔽掉一些确还是如此(内心充满了疑惑ing...),偶然在重构另一个页面时,发现了猫腻。重构的是一个商品分类页面,左边是上下滑动的分类标签,右边展示对应标签查询出的商品列表,使用了对象数组存储每个分类标签下的相关数据,结构如下:

data: {
    pageData: [], // 存储每个分类对应的数据
    defaultParams: { //pageData数组对应的对象数据结构     
        categoryId:'',//分类ID
        list: [] //存储商品列表
        //其他字段
        requesting: false, 
        end: false, 
        empty: false, 
        pageNum, 
        pageSize: 20, 
    }, 
}
复制代码

之所以使用了数组,为的是存储点击对应分类后请求的接口数据,当重复点击某个点击过的分类时,不用再次请求接口,根据分类索引从数组中取商品列表直接渲染即可,是一种优化手段。

3.gif 分类页

可以看到分类页点击标签后的交互,第一次是加载数据,第二次再回去点击直接显示出列表内容

说这么多是因为首页也是由分类和商品列表两部分组成的,而存储的数据也是pageData,俩页面如此相似,分类页却没有卡顿的问题。作为主角的首页,当然要比分类页要多点骚操作才能显示出它的地位,而这个骚操作就是左右滑动整个页面来切换分类模块

1.gif 首页

分类页只有点击分类标签切换,没有滑动的交互。

而左右滑动切换模块用到的是swiper组件,分类的数据大约20个左右,也就是说需要初始化20个swiper-item,它里面包含的内容就是商品列表,在微信开放社区搜了下这个组件,果不其然大家都遇到了这个组件数量太多页面卡顿的问题。一开始并没有立马怀疑swiper,有一个关键点让我慢慢接近这个怀疑对象:分类页的pageData在页面初试完后并没有根据分类的个数初始化完所有的数据,也就是说首页上有20个分类,就初始化了20个数组元素,而是在分类标签点击后初始化对应索引下的数据,因为没有必要初始化所有的。

这里就与首页的不同了,翻了翻首页的代码,发现居然上来就初始化了所有的数据,首页除了这个分类商品列表,没有比这数据更多的内容了,会不会是因为这个pageData呢,顶着一个大大的问号和一点点零星的期待,去修改了首页的初始化逻辑,改成与分类页一样,点击分类后初始化pageData索引下的数据,果不其然跟我预料的一样,页面加载完后不卡了,立马点击滑动啥的操作也变得无感,吼吼好开心,终于让我逮到原因了,原来是这个pageData数组数据太多导致的呢,

二话不说,改成跟分类一样的初始化逻辑即可,but....问题也随之出现,点击分类导后倒是没问题,但忽略了页面左右滑动这个交互,然后就.....滑不动了(内心苦涩.....),就像这样:

5.gif

滑不动是因为数据未初始化,从而导致swiper-item未渲染,swiper只有在swiper-item有的情况下才能滑动.....这就尴尬了,这跟点击哪个初始化哪个的方法冲突了。

解决方法

既要保证pageData数组元素初始化的少,又要保证左右滑得动,用分页的方法或许可以,经过我一番各种尝试,终于有了结果。好了,不啰嗦,说下思路吧:点击分类tab或滑动swiper时都需要初始化并加载数据, 点击分类tab会触发一个点击事件,滑动swiper会触发滑动事件,那就在这两个事件触发时初始化pageData,而整个页面初始化后可以先加载三四个swiper-item(因业务逻辑不同,我这里试过页面一开始只加载三四个时还是不卡的)

看下方法:


data:{
    current0,//选中的分类索引
    pageTypes: [{name:'',id:'',index:0}],//分类数据
    pageData: [], //列表数据
    defaultParams: { //pageData里的对象
        categoryId'',
        requestingfalseendfalseemptyfalse, 
        pageNum, 
        pageSize20,
        list: [] 
    },
            
},
//初始化当前的分类tab与前一个后一个的数据,current是选中的分类tab的索引
_loadRemainPageData(current) {

        const { pageTypes, pageData, defaultParams } = this.data
        //根据选中的分类索引,取前后范围,截取分类数组,用这段数组来初始化pageData
        let typeFragment = pageTypes.slice(current - 1, current + 2)

        let addNum = 0

        typeFragment.forEach(item => {
            //pageType的数据里有索引值index,它与pageData是一一对应的,所以可以取出来初始化对应的数据
            if (!this.data.pageData[item.index]) {//防止重复初始化

                addNum++ //记录每次截取pageType后初始化的个数,初始化过后不会走这

                pageData[item.index] = {

                    ...defaultParams,

                    categoryId: item.id
                }

            }

        })

        if (addNum > 0this.setData({ pageData })

    }
复制代码

这个方法用在这两个事件里:

    //分类tab切换事件
    hanldeTabType(e) {

        let { index, item } = e.detail

        this.setData({

            current: index,

            categoryId: item.id, 

        })

        let currentPageData = this._getCurrentData()

        //点击分类tab切换页面时去初始化pageData对应索引下的值

        if (!currentPageData) {

            this._loadRemainPageData(index)

            currentPageData = this._getCurrentData()

            if (currentPageData.list.length === 0this._getSpuList('refresh', pageNum)

        }

    },
    //页面滑动事件
    handleAnimationFinish(e) {

        const current = e.detail.current

        this.setData({

            current,

        })

        this._loadRemainPageData(current)

        let curpageData = this._getCurrentData()

        if (curpageData.list.length === 0this._getSpuList('refresh', pageNum)

    },
    /**

     * @description: 获取当前激活页面的数据

     * @param {*}

     * @return {*} 当前索引对应的数据

     */

    _getCurrentData() {

        return this.data.pageData[this.data.current]

    },
复制代码

这两个事件去执行初始化方法,不论是从前往后点击分类,还是点击分类的倒数第几个,都能保证pageData对应的数据初始化完成,swiper也能一直滑得动。滑不动的问题解决了,也用分页的方法解决了卡顿问题,但

当数据量非常多的时候且从分类的倒数第几个点击时,还是会因为pageData太多在点击之后卡顿,因为,比如点击倒数第19个时,pageData的数据是这样的:

图片.png

而渲染后的DOM是这样的:

图片.png

因为数组的长度变了,还是会渲染swiper-item, 在微信社区看到一篇文章,把没有显示出来的DOM尽量简化,用空的swiper-item替代之前复杂的那个

图片.png

这样就更快了,页面初始化完后,点击倒数几个分类出现的卡顿也基本无感了

小结

不过当数据非常多时,点击倒数第几个分类时还是会有卡顿的问题,但以上方法足以解决线上很卡的性能问题。嗯...目前还没想到更好地方法,若有更好地方法希望大家可以分享出来互相学习嘻嘻

有新发现

在做一个H5页面时,也遇到了需要使用很多swiper的情况(说的是swiper滑块很多...),这次swiper的每个滑块中包含的是一个Echarts图表,当数据少时页面加载完后手指滑动swiper时还是可以的,当数据量多时,整个页面就卡住了,先来看下这个页面吧

页面包含多个swiper的优化1.gif

(此处啰嗦一哈:看起来并不是我说的很卡对吧,那当然是我优化后的样子啦哈哈,至于卡顿的就不拿出来了,因为页面是不动的,就算有动图看起来也是个截图的样子🐶)

温度与湿度两个模块组件中,分别使用了两个swiper,每个swiper中有11个滑动容器,再加上内容是Echarts,页面肯定会很卡。优化后的页面看起来流畅多了,页面初始化时依然初始化了很多数据来渲染swiper滑块,且这次并没有用上次那么复杂的方法,而是把滑块中的内容做了if判断,滑块组件正常渲染即可,内容在当前滑块所处的index与数据list的index相等时才渲染,也就是说除了当前index的滑块,其他滑块的内容都是空的

实时看板.png

数据多时导致页面卡顿的原因当然与数据多少有很大关系,但主要的原因还是滑块中的内容,内容多且DOM复杂时卡顿就很明显了,一两个看不出来,五六个时就明显了,为啥这么说呢,因为我试着把滑块中的内容用最简单的文字作为替代,发现虽然同样滑块很多,但页面依旧很流畅,如下:

页面包含多个swiper的优化2.gif

这次虽然不是小程序的swiper,但也有着同样的问题,而这次的问题与上次小程序页面稍有不同,上次小程序是逐渐把数据分页填充,最后即时有很多滑块包括滑块内容复杂,页面只要渲染过一次后便可以流畅操作,而这次就算页面初始化后好久也一直卡顿,整个页面就像崩了一样~~上次的方法用在这次显示不合适喽....

还是要根据实际情况选择合适的解决方法,嗯,这次就说这么多吧,说不定哪天我又会发现更不错的解决方法了嘿嘿(2022-11-05记)

好记性不如烂笔头,还是选择记录一下,哪天估计又忘记了🐶

总结文章挺耗时间的,看完觉得有用可以给个小小的赞奥,比心 ^-^

分类:
前端
标签: