评论

微信小程序答题页——swiper渲染优化及swiper分页实现

答题类型小程序,swiper加载太多的优化,去除swiper切换动画,swiper分页

前言

swiper的加载太多问题,网上资料好像没有一个特别明确的,就拿这个答题页,来讲讲我的解决方案

这里实现了如下功能和细节:

  1. 保证swiper-item的数量固定,加载大量数据时,大大优化渲染效率
  2. 记录上次的位置,页面初次加载不一定非得是第一页,可以是任何页
  3. 答题卡选择某一index回来以后的数据替换,并去掉swiper切换动画,提升交互体验

示例动图

截图

问题原因

当swiper-item数量很多的时候,会出现性能问题

我实现了一个答题小程序,在一次性加载100个swipe-item的时候,低端手机页面渲染时间达到了2000多ms

也就是说在进入答题页的时候,会卡顿2秒多去加载这100个swiper-item

思考问题

那我们能不能让他先加载一部分,然后滑动以后再去改变item的数据,让swiper一直保持一定量的swiper-item?

注意到官方文档有这么两个属性可以利用,我们可以开启衔接滑动,然后再bindchange方法中去修改data

1、保证swiper-item的数量固定,加载大量数据时,优化渲染效率

假设我们请求到的数据的为list,实际渲染的数据为swiperList

我们现在给他就固定3个swiper-item,前后滑动的时候去替换数据

正向滑动的时候去替换滑动后的下一页数据,反向滑动的时候去替换滑动后的上一页数据

当我们知道了要替换的条件,我们便可以去替换数据了

但是我们应该考虑到临界值的问题,如果当前页是list第一项和最后一项该怎么办,向左向右滑是不是得禁止啊

这边是判断没数据会让它再弹回去

2、记录上次的位置,页面初次加载不一定非得是第一页,可以是任何页

有很多时候,我们是从某一项直接进来的,比如说上次答题答到了第五题,我这次进来要直接做第六题

那么我们需要去初始化这个swiperList,让它当前页、上一页、下一页都有数据

3、答题卡选择某一index回来以后的数据替换,并去掉swiper切换动画,提升交互体验

从答题卡选择index,那就不仅仅是滑动上下页了,它可以跳转到任何页,所以也采用类似初始化swiperList的方法

swiper切换动画我这边是默认250ms,但是发现有时候从答题卡点击回来,你在答题卡点击的下一项不知道会从左还是从右滑过来

体验真的很差,一开始不知道怎么禁掉动画,其实在跳转到答题卡页的时候把duration设为0就可以了

然后在答题卡页的unload方法中恢复

关键点:

在固定3个swiper-item的同时,要保证我们可以有办法来替代微信自带swiper的current属性和change方法

swiper-limited-load使用方法及说明:

  • 将components中的swiper-limited-load复制到您的项目中
  • 在需要的页面引用此组件,并且创建自己的自定义组件item-view
  • 在初始化数据时,为你的list的每一项指定index属性
  • 具体可以参照项目目录start-swiper-limited-load中的用法
  • 说明:其它属性和swiper无异,你们可以自己单独添加你们需要的属性

总结

一开始很头疼,为什么微信小程序提供的这个swiper,没去考虑这方面

然后在网上和社区找也没有一个特别好的解决方案。

后来想想,遇到需求就静下来解决吧。

项目地址:https://github.com/pengboboer/swiper-limited-load

如果错误,欢迎指出。

如有新的需求也可以提出来,如果有时间的话,我会帮你们完善。

如果能帮到你们,记得给一个star,谢谢。

---补充

有很多朋友在评论区提到了分页的需求,抽时间写了一个分页的Demo和大家分享一下。

还是以答题为例,比如我们一共有500条数据,一页20条,可能需要如下功能,乍一看不就加了个分页,挺简单的,其实实现起来挺麻烦的,下面说一下思路和一些需要特别注意的点:

1、从其他页面跳转到答题页时,不光只能默认在第一题,可以是任意一题,比如第80题。

跳转到任意一题,那么需要我们根据index算出该数据在第几页,然后需要请求该页数据,最后显示对应的index。我的思路更注重用户体验,不可能是上滑或者下滑才开始去请求数据,一定是要用户滑动前提前请求好数据。所以起码要保证左右两侧在初始化那一刻都有数据。如果此题和它的上一题下一题都在同一页,那么我们只需要请求一页数据(第15题,那么只需请求第1页数据)。如果此题和它的上一题或者下一题不在同一页,那么我们可能需要请求两页数据。(第20题,那么需要请求第1页和第2页数据)

2、左滑、右滑没数据时,都可以加载新数据。直到滑到第一题或者最后一题。

如果我们初始化时是第24题,那么我们左滑到第21题时,就应该去请求第一页的数据。那么用户在看完21题时,再滑到20题,可能就根本不会感知到通过网络请求了数据。但是如果用户此刻滑动特别快:滑到21题时请求了网络,请求还没成功,就又向左滑了。那么我们需要限制用户的滑动,给用户一个提示:数据正在加载中。

3、从答题卡点击任意一题可以跳转到相应的题目,并且左右滑动显示正常数据

比如我们初始化是跳转到了第80题,不一会点击答题卡又要跳转到200题,一会又跳转到150题。各种无序操作,你也不知道用户要往哪里点。

一开始是想着维护一个主list,点到哪道题往list中添加这道题所在的当页的数据,但是还得判断这一页或者左滑右滑请求新一页的数据得往list的哪个位置添加。这来回来去乱七八糟的判断就很麻烦了,很容易出bug。而且list长度太长了以后insert的性能也不好。

后来就去想,要不答题卡点击任意一题都清空旧的list,然后请求新的数据,左右滑动没数据了再请求新的数据呗。但是这样很浪费资源,并且用户体验也不好,用户已经从第1题答到第200题了,这时用户从答题卡选择了一个25题,还得重新请求网络。而且200道题的数据都没了,那再选个26题,再重新请求网络?网络有延时不说,还浪费资源。

最后转念一想,这时候就需要弄一个缓存了。所以最终的解决方法就出来了:我们维护一个map,在网络请求成功后,在map中保存对应页的数据,同时我们维护一个主list来显示对应的题目。当我们在答题卡选择某一题目,就清空list,然后判断map中有没有该页的数据,如果有就直接拿来,没有就再去网络请求。这个处理方式,写法相对来说简单,不需要乱七八糟的判断,也不浪费资源,用户体验也很不错。

总结

以上就是一些思路和要注意的地方。这个Demo断断续续花了好几天时间写出来的。可能我说的比较啰嗦比较细,只是想让需要用到这个分页Demo的同学能理解我是如何实现的。

如果觉得能帮到你,记得给一个star,谢谢。同时如果这个demo有bug或者你们有新想法,欢迎提出来。

最后一次编辑于  2021-01-07  
点赞 27
收藏
评论

25 个评论

  • pengboboer~😉
    pengboboer~😉
    2021-01-02

    因为有很多人有分页的需求,新增swiper分页demo,欢迎大家指正。

    2021-01-02
    赞同 1
    回复 1
    • 青春的短暂
      青春的短暂
      2021-03-18
      Git链接好像打不开了
      2021-03-18
      回复
  • 夜神月
    夜神月
    2021-05-28

    请问大佬有空能出一个uni-app版的吗,我在change事件和watch里面动态修改current的值实在无效,无法照着你这个代码修改😭

    2021-05-28
    赞同 8
    回复 2
    • 阿斯顿
      阿斯顿
      2021-12-01
      大佬解决了吗?我这也一样
      2021-12-01
      回复
    • Volcano
      Volcano
      2023-03-05
      this.$nextTick(()=>{this.current=xxx})
      2023-03-05
      回复
  • 没尾巴的跳跳虎
    没尾巴的跳跳虎
    2022-09-24

    uni-app 的可以看看我基于楼主的思路改写的 https://gitee.com/mwbdtth/swiper-limited-load-uni-app

    2022-09-24
    赞同 3
    回复 3
    • 陈小懒
      陈小懒
      2022-10-26
      大佬,你的demo考下来直接运行报错了  请问这个是什么问题
      2022-10-26
      回复
    • 没尾巴的跳跳虎
      没尾巴的跳跳虎
      2022-11-14回复陈小懒
      你把,调用 初始化 swiper 的函数放在 onReady 生命周期函数中就行了,因为你放在 onLoad 函数中,页面上 dom 还没渲染完毕。
      2022-11-14
      回复
    • L
      L
      03-23
      大佬,滑到最后一个item的时候,往swiperList里添加新的数据然后再init,为啥索引就错了,滑动就都是乱了
      03-23
      回复
  • Andy༻
    Andy༻
    2021-07-28

    请问大佬有空能出一个uni-app版的吗

    2021-07-28
    赞同 3
    回复
  • 厘泡
    厘泡
    2022-04-07

    答题卡页面题号答错显示红色,答对显示绿色,怎么实现呀?用 if 和 setData 搞了好久,不是全绿就是全红,设置了索引下标也搞不来

    2022-04-07
    赞同 1
    回复 3
    • pengboboer~😉
      pengboboer~😉
      2022-04-25
      这个很简单的吧,解决了吗?
      2022-04-25
      回复
    • 秋酿
      秋酿
      2022-08-27
      可在每个对象增加一个布尔值,渲染判断布尔值
      2022-08-27
      回复
    • 阿星
      阿星
      2022-11-29
      这个我也不懂搞
      2022-11-29
      回复
  • 哈哈哈
    哈哈哈
    2021-01-06
    <swiper-item wx:key="sss" wx:for="{{ swiperList }}">
      <item-view item="{{ item }}" swiperHeight="{{ swiperHeight }}" />
    </swiper-item>
    


    你好,组件中swiper-item 的wx:key为什么要写死呢?

    2021-01-06
    赞同 1
    回复 3
    • pengboboer~😉
      pengboboer~😉
      2021-01-07
      我不想看到报警告所以随便写了一个,你也可以不写wx:key
      但是不要写具体的属性,比如wx:key="index",写了swiper的动画会消失
      2021-01-07
      回复
    • 哈哈哈
      哈哈哈
      2021-01-22回复pengboboer~😉
      嗯好 谢谢
      2021-01-22
      回复
    • 张彰 - 比链资本
      张彰 - 比链资本
      2023-01-31
      key很重要的,key不对或写死会导致页面抖动刷新,看wx:for的说明
      2023-01-31
      回复
  • 程以叁
    程以叁
    2020-04-26

    快速点击上一题会左滑下一题,这是啥情况

    2020-04-26
    赞同 1
    回复 10
    • pengboboer~😉
      pengboboer~😉
      2020-04-26
      嗯,我发现是有这个问题的,我抽时间修复一下吧
      2020-04-26
      回复
    • 程以叁
      程以叁
      2020-04-26回复pengboboer~😉
      大佬有空能分享一下 你写的到最后一题的判断逻辑吗,就是到了最后一个swiper-item再次滑动展示提示框那个
      2020-04-26
      回复
    • pengboboer~😉
      pengboboer~😉
      2020-04-26回复程以叁
      代码里应该有吧,你看看
      2020-04-26
      回复
    • 程以叁
      程以叁
      2020-04-26回复pengboboer~😉
      写在一起 没分清哪个是替换的item那块是判断再次滑动
      2020-04-26
      回复
    • 程以叁
      程以叁
      2020-04-26回复程以叁
      QuestionNum是从1开始的  我改了一下  然后isFirstPlaceholder就开始报错  是我的逻辑有问题吗,没看懂你的代码  老哥,有空可以回复下我嘛
      2020-04-26
      回复
    查看更多(5)
  • 小肥羊🍊
    小肥羊🍊
    2020-01-13

    非常感谢,我也开发了一个在线答题的小程序,有计划把答题的过程用swiper改造下,看来这就是我要找的答案了,再次感谢。

    2020-01-13
    赞同 1
    回复 3
    • pengboboer~😉
      pengboboer~😉
      2020-01-13
      我半小时前刚写的,这么巧吗?兄弟,哈哈!
      2020-01-13
      回复
    • pengboboer~😉
      pengboboer~😉
      2020-01-13
      有问题可以交流
      2020-01-13
      回复
    • 哈哈哈
      哈哈哈
      2021-01-08
      好的谢谢
      2021-01-08
      回复
  • 虾仁鱼🍤🚶🐠
    虾仁鱼🍤🚶🐠
    09-22

    发现一个bug,在「start-swiper-limited-load」组件中,点击答题卡,从「第1题」跳转到「3的倍数的题」的时候会跳转失败,current会变化但是题目不会跳转。

    09-22
    赞同
    回复 2
    • 虾仁鱼🍤🚶🐠
      虾仁鱼🍤🚶🐠
      09-22
      点击答题卡从「最后一题」跳转到「第一题」也存在问题
      09-22
      回复
    • 虾仁鱼🍤🚶🐠
      虾仁鱼🍤🚶🐠
      09-22
      问题的原因如下:
      1.初始化 swiperList:
      当 currentIndex = 0 时,swiperList 被初始化为 [Item0, Item1, null]。
      swiperList[2](即索引为 2 的位置)为 null,因为在初始化时,没有为其赋值。这对应于 Item0 的上一个项,但由于 Item0 是第一个项,所以不存在上一个项。
      2.currentIndex 的变化:
      当将 currentIndex 更改为 2 时,current = 2 % 3 = 2。
      此时,swiperList[current] 为 null,因为 swiperList[2] 在初始设置中没有被正确初始化。
      3.observers 函数中的提前返回:
      条件 if (swiperList.length == 0 || swiperList[current] == null) 成立,因为 swiperList[current] 为 null。
      这导致函数提前返回,轮播组件无法更新为新的项目。

      解决方案:
      要解决此问题,需要修改 current 属性的 observers 函数,移除 swiperList[current] == null 的检查,以防止函数在 swiperList[current] 为 null 时过早返回。删除这部分代码会有什么bug还不太清楚,但是至少解决问题了
      09-22
      回复
  • 右耳朵猫
    右耳朵猫
    03-09

    我在小程序【小而美工具】中实现了无限滑动的效果,只需要3个 <swiper-item> 即可,假设初始 current 为 1,以日历的月份滑动为例,大概思路如下:

    一、判断滑动的左右方向

    swiper 左滑时 current 的变化规律是 1 0 2 1 0 1

    swiper 右滑时 current 的变化规律是 1 2 0 1 2 0

    const direction = ['10', '02', '21'].includes(`${previous}${current}` ? 'left' : 'right'
    

    二、根据滑动方向,更新 3 个 <swiper-item> 的显示顺序

    确保 current 对应的 <swiper-item> 始终在中间,current-1 和 current+1 对应的 <swiper-item> 分别在左侧和右侧

    const newSwiperItem = []
    newSwiperItem[(current - 1 + 3) % 3] = selectedMonth - 1
    newSwiperItem[current] = selectedMonth
    newSwiperItem[(current + 1) % 3] = selectedMonth + 1
    // 渲染 newSwiperItem
    

    三、大功告成

    下面是最终的滑动效果,只用了3个<swiper-item>实现无限滑动

    03-09
    赞同
    回复

正在加载...

登录 后发表内容