hzwinner 2019-06-27
对于 swiper
,只要做过轮播图的童鞋应该都再熟悉不过了。这是一个很强大的图片轮播插件,本身无任何第三方库依赖,即插即用。api 文档很清晰,所以很快能够上手。但是,再好的插件也会出现令人不愉快的地方,当然,今天所讨论的并不是插件本身的问题,只是开发者是按照常规做法去使用,而恰好此时出现了令人费解的问题。
在使用 swiper
这个库的时候,一旦设置 loop:true
的时候,会遇到 dom 绑定事件无法触发的问题。
● vuejs 2.5.16
● swiper 4.3.3
● vue-awesome-swiper 3.1.3
下面来说说我是怎么一步一步采坑并最终解决这个问题的。这里只贴出关键性的代码片段。
这是最常规的做法,把 click 事件绑定在 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" @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); } } });
解决了上述两个问题,但同时也存在以下几个问题
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。
Vue和React是数据驱动视图,如何有效控制DOM操作?能不能把计算,更多的转移为js计算?因为js执行速度很快。patch函数-->patch,对比tag,对比tag与key,对比children