vue-swiper-virtual

在 vue 中使用 swiper 的 virtual 特性

swiper 介绍

  • 前端开发中,经常会碰到轮播、翻页等需求。swiper就是用来解决此类需求的。
  • 功能全、覆盖场景多,可适配移动端等特点,让它成为实现此类需求的最好选择。
  • 它有专门的vue版本,vue-awesome-swiper

背景

  • 项目是基于vue全家桶开发的,有轮播需求,轮播的 slides 数量较多,slide 的 DOM 结构比较复杂。

问题

  • 项目中使用的是vue-awesome-swiper,它基于swiper做了相关封装
  • 使用时并没有使用virtual slides特性,初始化时会渲染所有slide,导致初始渲染速度较慢。在DOM结构复杂,slide 数量为 100 时,初始渲染大概需要 2~3s。
  • 数据改变触发更新时,vue-awesome-swiper会频繁触发update操作。容易页面卡死。
  • 类似问题https://github.com/surmon-china/vue-awesome-swiper/issues/424
  • 见源码
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
// vue-awesome-swiper/src/slide.vue
<template>
<div :class="slideClass">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'swiper-slide',
data() {
return {
slideClass: 'swiper-slide'
}
},
ready() {
this.update()
},
mounted() {
this.update()
if (this.$parent && this.$parent.options && this.$parent.options.slideClass) {
this.slideClass = this.$parent.options.slideClass
}
},
updated() {
this.update()
},
attached() {
this.update()
},
methods: {
update() {
if (this.$parent && this.$parent.swiper) {
this.$parent.update()
}
}
}
}
</script>

-

使用 swiper 的 virtual 特性

  • virtual官方介绍 -虚拟Slide会在Dom结构中保持尽量少的Slide,只渲染当前可见的slide和前后的slide,而隐藏其他不可见的Slide,每次切换时就渲染新的Slide。当你的Slide很多的时候,尤其是Slide中有复杂的Dom树结构时,性能会有很大的提升。
  • 由于使用vue-awesome-swiper出现了一些性能问题,所以我们决定直接使用swiper版本并开启virtual特性(注意使用 4.0 版本
  • 中文文档中,并没有详细介绍如何在vue中使用virtual特性。
  • 英文文档 http://idangero.us/swiper/api/#virtual中详细介绍了如何在vue、react中使用virtual特性
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
59
60
61
62
<template>
<!-- ... -->
<div class="swiper-container">
<div class="swiper-wrapper">
<!-- 必须在每个slide上设置left -->
<!-- 勿直接将v-for的index做为key,会出现key重复的问题 -->
<div class="swiper-slide"
v-for="(slide, index) in virtualData.slides"
:key="slide.id"
:style="{left: `${virtualData.offset}px`}"
>
{{curIndex}}
<br/>
{{slide}}
</div>
</div>
</div>
<!-- ... -->
</template>
<script>

import Swiper from 'swiper/dist/js/swiper.esm.bundle' // 导入swiper
import 'swiper/dist/css/swiper.min.css' // 记得导入样式

export default {
data() {
return {
curIndex:0, // 当前翻页索引
// 声明virtualData,供swiper使用
virtualData: {
slides: [],
},
}
},
mounted() {
Api.getSomeSlides().then(res=>{
const self = this;
const swiper = new Swiper('.swiper-container', {
// ...
virtual: {
slides: res.slides, // 需要添加的虚拟slide的内容
renderExternal(data) {
console.log(data)
// 返回一个经过swiper计算后的当前需要渲染的slides相关信息
// offset - slides偏移值
// from - 首个需要渲染的slide索引
// to - 最后一个需要渲染的slide索引
// slides - 需要渲染的slides
self.virtualData = data
},
},
on:{
slideChange(){
// 更新index
self.curIndex=this.activeIndex
}
}
});
})
},
};
</script>
  • 使用后每次仅渲染当前页及前后页
  • 每次渲染时,会重新触发vue的生命周期
  • 其他使用基本和不使用virtual特性保持一致
  • 若有swiper嵌套需求,可在子组件中直接实例化一个swiper并设置nested选项为true

总结

  • vue-awesome-swiper有坑,会频繁调用update,导致一定性能问题
  • 可直接使用swiper替换vue-awesome-swiper

相关链接