UzumakiItachi
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

UzumakiItachi

起风了,唯有努力生存。
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • WebPack

  • 浏览器相关

  • 工程化相关

  • 工作中遇到的问题以及解决方案

    • 解决浏览器返回页面不刷新的问题
    • 前端如何下载文件流
    • uniapp APP端实现更新最新安装包
    • GitHub Actions Process completed with exit code 128 的解决方案
    • 保留文字输入的空格和换行
    • 缩放适配大屏页
    • 一次性加载n多张图片的性能优化方案
    • NOT-Cool 低代码页面架构思路
    • h5下载vcard快捷保存联系人信息
    • 结合elementui实现的动态主题
    • 解决monorepo场景下子包作为依赖项在开发和生产暴露文件的问题
    • 给npm配置github令牌
    • 移动端电子印章解决方案
    • 小程序canvas绘制海报中遇到的一些坑
    • 超详细的虚拟列表实现过程
    • 小程序实现一个事件中心
    • 请求超时后如何优雅的重新请求
    • 超详细的大文件上传实现方案
    • 仿抖音短视频组件实现方案
    • swiper渲染大量数据的优化方案
      • 简易版优化
      • 进阶版优化
      • 超级版优化
      • 总结
    • 前端检测更新,自动刷新网页
  • Git

  • Vite

  • 一些小工具

  • 算法

  • 服务器

  • HTTP

  • 技术
  • 工作中遇到的问题以及解决方案
hanhanbuku
2025-06-06
目录

swiper渲染大量数据的优化方案

最近都在开发小程序相关业务,许多交互都需要类Swiper这种效果。秉持着不重复造轮子的原则,能用Swiper实现的都用Swiper实现了嘿嘿。但是过程中也暴露出一些问题,在某些数据量很大的场景下会渲染出多个swiper-item,比如仿抖音那种刷视频效果,以及答题的业务下。都会涉及到数据量庞大,Swiper-item渲染过多导致页面卡顿的问题。优化行动刻不容缓!下面就一起来看看该如何优化吧。

# 简易版优化

提示

先上个开胃菜,来个简易版的。大体的思路就是忽略swiper-item的数量,只渲染当前项的内容,非当前项的swiper-item内容为空。

上代码

export default function SwiperCard(){
    const [currentIndex,setCurrentIndex] = useState(0)
    const [list,setList] = useState([])
    
    return (
        <Swiper
            className='video-swiper'
            vertical
            current={currentIndex}
        >
            {
                list.map((item,index)=>{
                    return (
                        <swiper-item>
                            {
                                currentIndex===index&&<div>内容...</div>
                            }
                        </swiper-item>
                    )
                })
            }
        </Swiper>
    )
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

这个比较简单 就不过多解释了。下面来看看进阶版

# 进阶版优化

提示

简易版已经可以满足大部分场景,如果还要追求进一步优化的话就要从swiper-item的数量入手了。下面我们将只渲染3个swiper-item,通过替换数据的方式来实现无限轮播

export default function SwiperCard(){
    const [currentIndex,setCurrentIndex] = useState(0)
    const [list,setList] = useState([])
    
    return(
        <Swiper
            className='video-swiper'
            vertical
            current={currentIndex}
        >
            {
                list.map((item,index)=>{
                    return (
                        <swiper-item>
                            {
                                currentIndex===index&&<div>内容...</div>
                            }
                        </swiper-item>
                    )
                })
            }
        </Swiper>
    )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

首先我们渲染的数据就不再是整个list了,而是当前渲染项加上前一个和后一个数据组成的长度为3的数组。所以我们要动态的计算出这个值

const [currentIndex,setCurrentIndex] = useState(0)
const [displayList,setDisplayList] = useState([]) // 真实渲染的数据
const [originList, setOriginList] = useState([]) // 原始数据
const [displayIndex,setDisplayIndex] = useState(0)// 当前展示项在原始数据中的索引

// 动态计算真实渲染的数据
useEffect(() => {
    // 处理边界情况
    if (displayIndex === 0) {
        const nextList = originList.slice(0, 3)
        setDisplayVideos([
            ...nextList
        ])
        return
    }
    if (displayIndex === originList.length - 1) {
        const nextList = originList.slice(-3)
        setDisplayVideos([
            ...nextList
        ])
        return
    }
    setDisplayList([
        originList[displayIndex - 1],
        originList[displayIndex],
        originList[displayIndex + 1],
    ])
}, [displayIndex]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

这里我们引入了两个个新变量来存储真实渲染的数据和当前展示项在原始数据中的索引,并且通过监听displayIndex的变化 动态计算DisplayList的值。页面上不再渲染list,而是渲染DisplayList。

现在我们已经实现了只渲染3个swiper-item,还差点什么呢?仔细想想...对了!还没有更新currentIndex的值呢。这一步就简单了我们只需要在swiper的change事件里去更新currentIndex的值就可以了

const handleSwiperChange = (e)=>{
    const { current } = e?.detail
    if(current===0){
        ...
    }else if(current===1){
        ...
    }
    
}

1
2
3
4
5
6
7
8
9
10

问题来了,我们该如何确定swiper是上滑了还是下滑了呢?这关系到displayIndex是+1还是-1。细心的朋友应该猜到了,可以通过change事件的current参数来判断是上滑还是下滑,如果为0那就是下滑了,displayIndex就需要-1。反之如果current为2就是上滑,displayIndex就需要+1。

那问题又来了,我们只有3个swiper-item,也就是说只能上滑或者下滑1次,最多两次。所以我们还需要实现无限滑动。

要实现无限滑动,我们就需要在滑动之后修正current,也就是说,每次上滑或者下滑之后我们需要把current修正为1。这样就可以又进行下一次上滑or下滑

首先我们需要分析一下,总共需要处理三种情况,分别是当current为0,1,2的时候。我们都需要去更新真实渲染索引的值,也就是displayIndex的值。

const handleSwiperChange = (e)=>{
    const { current } = e?.detail
    
    if(current===0){
        // 向上滑动,显示上一个
        setDisplayIndex(prev => prev - 1)
    }else if(current===1){
        
    }else if(current===2){
        // 向下滑动,显示下一个
        setDisplayIndex(prev => prev + 1)
    }
    
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

0和2的情况比较好处理,我们直接对displayIndex进行加减操作就行了。1就比较特殊了,因为当我们修正索引为1的时候也会触发change事件,这种情况是不需要处理的,所以在判断中我们要加一个限制条件。只有当displayIndex为0并且current为1的时候(这时说明用户是从第一个选项卡往下滑),我们才给displayIndex+1,其余的current为1的时候我们视为是在修正current不做处理。 current为0和为2的时候也要考虑一下原始数据首位的边界情况,同样不做处理。然后再更新完displayIndex的值之后再把swiper的current修正为1

const handleSwiperChange = (e)=>{
    const { current } = e?.detail
    
    if(current===0 && displayIndex > 1){
        // 向上滑动,显示上一个
        setDisplayIndex(prev => prev - 1)
    }else if(current===1&&displayIndex===0){
        setDisplayIndex(1)
    }else if(current===2&&displayIndex<originList.length-1){
        // 向下滑动,显示下一个
        setDisplayIndex(prev => prev + 1)
    }
    // 重置当前索引为中间位置
    setCurrentIndex(1)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

这里逻辑可能有点绕,大家不要把displayIndex和current搞混了哦。current是swiper的选项卡的索引,因为我们只渲染三个所以他的值始终在0,1,2这三个值里切换,那为什么需要displayIndex呢?因为我们要通过displayIndex去动态计算出真实渲染的list的数据,也就是那三个选项卡的数据。

到这里我们就已经实现了一个可以无限滑动,并且只渲染3个选项卡的swiper了。如果跟着做的同学应该发现了,虽然我们的swiper可以无限滑动了。但是有一个问题,在我们修正swiper的索引的时候也会出现切换动画。这显然是我们不想看到的,所以接下来我们需要做的就是修正索引时让用户无感 我们要悄悄摸摸的替换掉数据

这里我们可以在修正的时候把swiper的动画时间置为0,等修正完毕后再改回来。

const [swiperDuration, setSwiperDuration] = useState(300) // 默认动画时间

const handleSwiperChange = (e)=>{
    const { current } = e?.detail
    // 记录当前索引
    setCurrentIndex(current)
    if(current===0 && displayIndex > 1){
        // 向上滑动,显示上一个
        setDisplayIndex(prev => prev - 1)
    }else if(current===1&&displayIndex===0){
        setDisplayIndex(1)
    }else if(current===2&&displayIndex<originList.length-1){
        // 向下滑动,显示下一个
        setDisplayIndex(prev => prev + 1)
    }
    
    // 重置动画时间为0,准备调整位置
    setSwiperDuration(0)
    // 延迟执行,确保状态更新
    setTimeout(() => {
        // 重置当前索引为中间位置
        setCurrentIndex(1)
        // 恢复动画时间
        setTimeout(() => {
            setSwiperDuration(300)
        }, 100)
    }, 50)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

同时这里还需要把change事件换成onAnimationFinish事件,等切换动画完毕后再修正索引。

是不是以为到此就结束了,NO NO NO!正真的好戏现在才开始。

# 超级版优化

先来回顾一下上面的两种方案:

  1. 渲染完整数量的swiper-item,但只渲染当前swiper-item的内容
  2. 只渲染3个swiper-item,通过不断替换数据以及修正current的方式让swiper-item一直处于1的位置,以此来从交互上骗过用户

极致的性能优化肯定受限就要pass掉第一种方案,第二种方案显然是更满足我们的要求的,但是实现过于复杂,那有没有一种方案既能渲染少量的swiper-item,又可以实现的比较简单呢?

话不多说 直接进入正题

思路解析

之前一直都在围绕swiper的current做文章,现在我们换一种思路,不去考虑current的问题。就渲染3个swiper-item,下面我们罗列一下需要解决的问题点

  1. 如何实现无限滑动?
    • 答:可以采用swiper的首尾衔接模式
  2. 如何首个swiper-item和末尾那个swiper-item不衔接滑动?
    • 答:动态设置circular属性
  3. 怎么只在当前swiper-item里渲染内容,其他两个swiper-item不渲染内容?
    • 答:首先我们需要维护一个内容索引,下文简称curContentIdx。这个curContentIdx就是我们要渲染的数据的索引,在swiper上滑和下滑的时候需要对这个索引进行对应的更新。每个swiper-item的索引都是唯一的0,1,2。当前展示的swiper-item的索引和curContentIdx的关系是curContentIdx % 3 = swiperItemIndex。这样一来就可以确定内容怎么渲染了

有了上文的思路 代码就很简单了

export default function VideoPage() {

    const [prevIndex, setPrevIndex] = useState(0) // 记录当前索引
    const [curContentIdx, setCurContentIdx] = useState(0) // 当前内容的索引
    const [circular, setCircular] = useState(false) // 是否循环播放
    const [swiperDuration, setSwiperDuration] = useState(100) // 默认动画时间
    const [originList, setOriginList] = useState<any[]>([]) // 原始数据列表

   // 添加滑动开始处理函数
   const handleSwiperChange = (e) => {
      const { current } = e?.detail
      let direction = '';
      if (prevIndex === 0 && current === 2) {
         direction = 'up';
      } else if (prevIndex === 2 && current === 0) {
         direction = 'down';
      } else if (current > prevIndex) {
         direction = 'down';
      } else if (current < prevIndex) {
         direction = 'up';
      }
      setPrevIndex(current);
      if (direction === 'down') {
         setCurContentIdx(prev => prev + 1);
      } else if (direction === 'up') {
         setCurContentIdx(prev => prev - 1);
      }
   };

   // 设置衔接滑动
   useEffect(() => {
      const isSw = !(curVideoIdx == 0 || curVideoIdx == originList.length - 1)
      setCircular(isSw)
   }, [curVideoIdx])

   return (
           <View className='video-page'>
              <Swiper
                      className='video-swiper'
                      circular={circular}
                      vertical
                      current={prevIndex}
                      onChange={handleSwiperChange}
                      duration={swiperDuration}
              >
                 {[0, 1, 2].map((video, index) => (
                         <SwiperItem key={index} className='video-swiper-item'>
                            <View className='video-container'>
                               {curVideoIdx % 3 === index && (
                                       <view>渲染内容-----</view>
                               )}
                            </View>
                         </SwiperItem>
                 ))}
              </Swiper>
           </View>
   )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

按照上文的思路去看代码,代码写的很简单。至此我们就完成了一个只有三个swiper-item,支持首尾数据禁止衔接滑动中间数据可以无限滑动,支持只在当前swiper-item里渲染内容的高性能swiper组件。

做到这里就已经大功告成啦!下面来总结一下!

# 总结

本文主要讲述了swiper渲染大量数据的优化方案,主要有三种方案

  1. 不关注swiper-item数量,只关注内容(完整渲染swiper-item,但是之渲染当前的swiper-item的内容)
  2. 只渲染3个swiper-item,通过不断修正current和替换渲染数据的方式实现无限轮播,并且只渲染当前swiper-item的内容
  3. 不从swiper本身下手,通过动态变更circular的方式实现无限轮播和首尾不衔接。通过计算当前内容项索引和swiper-item索引之间的关系来实现只渲染当前swiper-item的内容,代码简介,性能高(强推!)
编辑 (opens new window)
上次更新: 2025/06/13, 15:58:27
仿抖音短视频组件实现方案
前端检测更新,自动刷新网页

← 仿抖音短视频组件实现方案 前端检测更新,自动刷新网页→

最近更新
01
前端检测更新,自动刷新网页
06-09
02
仿抖音短视频组件实现方案
02-28
03
超详细的大文件上传实现方案
01-17
更多文章>
Theme by Vdoing | Copyright © 2023-2025 UzumakiItachi | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式