对于 swiper
,只要做过轮播图的童鞋应该都再熟悉不过了。这是一个很强大的图片轮播插件,本身无任何第三方库依赖,即插即用。api 文档很清晰,所以很快能够上手。但是,再好的插件也会出现令人不愉快的地方,当然,今天所讨论的并不是插件本身的问题,只是开发者是按照常规做法去使用,而恰好此时出现了令人费解的问题。
现象
在使用 swiper
这个库的时候,一旦设置 loop:true
的时候,会遇到 dom 绑定事件无法触发的问题。
环境
● vuejs 2.5.16
● swiper 4.3.3
● vue-awesome-swiper 3.1.3
采坑
下面来说说我是怎么一步一步采坑并最终解决这个问题的。这里只贴出关键性的代码片段。
第一版
这是最常规的做法,把 click 事件绑定在 dom 上。但有两点不足之处:
- click 点击事件有时候无任何反应,并且这种情况必现
- 点击下面的 title,并不能触发 click 事件,因为并没有绑定
html 代码
<div class="banner" v-if="bannerList.length">
<swiper :options="swiperOption" ref="mySwiper">
<swiper-slide
v-for="(banner, index) in bannerList"
:key="banner.id"
@click.native="handleClickSlide(index)">
<div class="banner-item">
<img :src="banner.imgUrl" alt="news">
<p>{{banner.title}}</p>
</div>
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
js 代码
new Vue({
el: '#app',
data: function () {
return {
swiperOption: { // 轮播配置
width: window.innerWidth,
autoplay: {
delay: 3000
},
loop: true, // 循环滚动
pagination: { // 分页器
el: '.swiper-pagination'
},
preventLinksPropagation: false // 阻止点击事件冒泡
},
bannerList: [
{
id: '1',
title: '世界杯揭幕战-超新星1球2助攻俄罗斯5-0沙特 格里兹曼宣布留马竞',
imgUrl: 'http://n.sinaimg.cn/sports/180/w640h340/20180615/AYes-hcyszrz3457297.jpg'
},
{
id: '2',
title: '颜值满分!世界杯首日美女球迷盘点',
imgUrl: 'http://n.sinaimg.cn/sports/180/w640h340/20180615/H3Wz-hcyszrz4804003.jpg'
},
{
id: '3',
title: '盘点历届世界杯大比分“屠杀”',
imgUrl: 'http://n.sinaimg.cn/sports/180/w640h340/20180615/FNuk-hcyszrz4805039.jpg'
}
]
}
},
methods: {
// 坑在这里:
// 会发现有的时候,click 事件点击无反应,而且这种情况是必现的
handleClickSlide(index) {
console.log('handleClickSlide current index', index);
}
}
});
改良的第二版
解决了上述两个问题,但同时也存在以下几个问题
- 常用的 activeIndex (用来标识当前点击的第几张图片),但控制台输出的值是错乱的
- 左右作切换的时候,activeIndex 的值也是错乱的
html 代码
<div class="banner" v-if="bannerList.length">
<swiper :options="swiperOption" ref="mySwiper" @click.native="handleClickSlide">
<swiper-slide
v-for="(banner, index) in bannerList"
:key="banner.id">
<div class="banner-item">
<img :src="banner.imgUrl" alt="news">
<p>{{banner.title}}</p>
</div>
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
js 代码
new Vue({
el: '#app',
data: function () {
return {
swiperOption: { // 轮播配置
width: window.innerWidth,
autoplay: {
delay: 3000
},
loop: true, // 循环滚动
pagination: { // 分页器
el: '.swiper-pagination'
},
preventLinksPropagation: false // 阻止点击事件冒泡
},
bannerList: [
{
id: '1',
title: '世界杯揭幕战-超新星1球2助攻俄罗斯5-0沙特 格里兹曼宣布留马竞',
imgUrl: 'http://n.sinaimg.cn/sports/180/w640h340/20180615/AYes-hcyszrz3457297.jpg'
},
{
id: '2',
title: '颜值满分!世界杯首日美女球迷盘点',
imgUrl: 'http://n.sinaimg.cn/sports/180/w640h340/20180615/H3Wz-hcyszrz4804003.jpg'
},
{
id: '3',
title: '盘点历届世界杯大比分“屠杀”',
imgUrl: 'http://n.sinaimg.cn/sports/180/w640h340/20180615/FNuk-hcyszrz4805039.jpg'
}
]
}
},
computed: {
swiper() {
return this.$refs.mySwiper.swiper;
}
},
methods: {
// 坑在这里
// 一开始点击第一张图片,控制台输出的 activeIndex 竟然是 1,难道不应该是 0吗?
// 并且一个循环之后,点击第一张图片, 控制台输出的 activeIndex 竟然变成了 4。。。
handleClickSlide() {
// 这个应该是最为想到一个属性,用来标识当前点击图片的索引
const {activeIndex} = this.swiper && this.swiper;
console.log('handleClickSlide current index', activeIndex);
}
}
});
最终版
通过 swiper 强大的 api 文档,解决了上述出现的几个问题。关键点在于:当 loop
设置为 true 的时候,不能再用 activeIndex
或者 clickedIndex
。只能用 realIndex
。官方的解释为:当前活动块的索引,与 activeIndex
不同的是,在 loop
模式下不会将复制的块的数量计算在内。
点击事件不能绑定在 dom
上
不过稍不注意,也会出现新的坑(代码里有指出)
html代码
<div class="banner" v-if="bannerList.length">
<swiper :options="swiperOption" ref="mySwiper">
<swiper-slide
v-for="(banner, index) in bannerList"
:key="banner.id">
<div class="banner-item">
<img :src="banner.imgUrl" alt="news">
<p>{{banner.title}}</p>
</div>
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
js 代码
let vm = null;
new Vue({
el: '#app',
data: function () {
return {
swiperOption: { // 轮播配置
width: window.innerWidth,
autoplay: {
delay: 3000
},
loop: true, // 循环滚动
pagination: { // 分页器
el: '.swiper-pagination'
},
on: {
click: function () {
// 这里有坑
// 需要注意的是:this 指向的是 swpier 实例,而不是当前的 vue, 因此借助 vm,来调用 methods 里的方法
// console.log(this); // -> Swiper
// 当前活动块的索引,与activeIndex不同的是,在loop模式下不会将 复制的块 的数量计算在内。
const realIndex = this.realIndex;
vm.handleClickSlide(realIndex);
}
},
preventLinksPropagation: false // 阻止点击事件冒泡
},
bannerList: [
{
id: '1',
title: '世界杯揭幕战-超新星1球2助攻俄罗斯5-0沙特 格里兹曼宣布留马竞',
imgUrl: 'http://n.sinaimg.cn/sports/180/w640h340/20180615/AYes-hcyszrz3457297.jpg'
},
{
id: '2',
title: '颜值满分!世界杯首日美女球迷盘点',
imgUrl: 'http://n.sinaimg.cn/sports/180/w640h340/20180615/H3Wz-hcyszrz4804003.jpg'
},
{
id: '3',
title: '盘点历届世界杯大比分“屠杀”',
imgUrl: 'http://n.sinaimg.cn/sports/180/w640h340/20180615/FNuk-hcyszrz4805039.jpg'
}
]
}
},
computed: {
swiper() {
return this.$refs.mySwiper.swiper;
}
},
created() {
vm = this;
},
methods: {
handleClickSlide(index) {
console.log('handleClickSlide current index', index);
}
}
});
希望借此可以帮助遇到此问题的小伙伴,祝大家的生活中再无 bug。