项目中多个地方用到轮播图,分别为首页顶部ListContainer组件中和底部Floor的组件中,对轮播图进行封装,就出现了一些Bug,记录这些踩过的坑。
首先下载swiper,注意: vue2不能用高版本
npm i swiper@5
引入swiper 和 css
import "swiper/css/swiper.min.css";
问题一:点击左右切换轮播图按钮功能失效?
在mounted中挂载生成dom元素后才new swiper,由于是封装组件,ListContainer(父组件)组件通过props数据传递数据给轮播图组件,但先触发的是轮播图组件的mounted,此时会渲染dom元素,却还没有拿到数据。
*解决办法:所以利用 watch监听 到父组件传递来数据才进行初始化。
问题二:虽然有数据但左右切换功能还是失效?
由于vue响应式更新是异步的,此时监视到数据变化,dep.notify就会通知watcher去更新视图图,watch就被塞到异步队列中,数据同步更新,但轮播图组件dom元素还未重新遍历更新。
*解决办法:利用 this.nextTick 等数据渲染成DOM元素加载完成后才触发(只会触发一次)。
问题三:轮播图移入停止移出播放功能出问题?
由于多处调用组件,复用的是同一个swiper实例,都通过类选择器找对用dom元素。
*解决办法:利用 下标、绑定ref 来进行区分不同的实例。
问题四:Floor组件中调用的轮播图自动轮播功能失效?
由于先对Floor组件进行遍历,然后又在每个Floor组件中调调轮播图组件,数据初始化为空数组,此时Floor组件根本还未遍历渲染。
等Floor组件请求到数据后把数据通过props传给轮播图组件,此时轮播图组件监视到的数据是无变化,所以new swiper不会触发。
*解决办法:使用 immediate: true 先初始化一次就可监视到数据变化。
性能问题:
1、immediate导致ListContainer组件中会多初始化一次,增加判断;
2、鼠标移入移出功能为了防止内存泄漏,摧毁事件。
3、如果数据放在vuex中,组件之间切换并不会数据不会卸载,增加判断。
附上最后完整代码:
<template>
<div class="swiper-container" ref="swiper">
<!-- 轮播图容器 -->
<div class="swiper-wrapper">
<!-- 轮播图 -->
<div class="swiper-slide" v-for="(img, index) in imageList" :key="index">
<img :src="img.imgUrl" :alt="img.imgName" />
</div>
</div>
<!-- 左右箭头 -->
<div class="swiper-button-next"></div>
<div class="swiper-button-prev"></div>
<!-- 小圆点 -->
<div class="swiper-pagination"></div>
</div>
</template>
<script>
// npm i swiper@5
// 引入swiper js和css
import Swiper from "swiper";
import "swiper/css/swiper.min.css";
export default {
name: "Carousel",
data() {
return {
isInit: false,
};
},
props: {
imageList: {
type: Array,
default: () => [],
},
},
watch: {
// 等imageList值发生变化才会调用
// 值发生变化就说明数据回来了
imageList: {
handler(imageList) {
// 让第一个轮播图第一次不要new Swiper
if (!imageList.length || this.isInit) return;
this.isInit = true;
// Vue响应式更新是异步更新,导致触发watch的时候,数据还未渲染成DOM元素
// DOM元素还未生成
// 等DOM渲染好才会触发
this.$nextTick(() => {
const mySwiper = new Swiper(this.$refs.swiper, {
// 左右箭头
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
// 小圆点
pagination: {
el: ".swiper-pagination",
},
// 自动轮播
autoplay: {
delay: 1000, // 自动轮播的时间
disableOnInteraction: false, // 用户操作完轮播图后,再次开启自动轮播
},
// 无缝轮播
loop: true,
});
mySwiper.el.onmouseenter = () => {
mySwiper.autoplay.stop();
};
mySwiper.el.onmouseleave = () => {
mySwiper.autoplay.start();
};
});
},
// 解决floor组件轮播图失效
immediate: true,
},
},
// mounted() {
// // new Swiper必须要等DOM生成
// // 等imageList数据回来
// },
};
</script>
<style></style>