编写可复用的组件,我们可以收获很多
2020/3/24 11:01:42
本文主要是介绍编写可复用的组件,我们可以收获很多,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
编写可复用的组件在实际开发中是非常常见的需求,根据需求的不同会有很多不同的组件出现,有简单的比如Alert这样的弹窗组件,也有复杂些的日历组件等等
今天我们一起来写写组件(Vue组件),看看从中能不能掌握什么知识点吧!!!
编写一个轮播图组件
轮播图组件想必是使用最多的组件之一了,尤其是每个首页当中必定会出现它的身影。
那么,我们就闲言少叙了,实现一个适用于移动端上的轮播图组件吧
先来爆照
效果图和项目的目录已经呈现在观众眼前了,接下来我们先从App.vue文件开始搞起先划个重点:要实现组件的开发,先简单看一下下面的3条逻辑
核心逻辑
- 初始化轮播图
- 自动播放
- 左右切换轮播图
工欲善其事必先利其器,首先不管怎样,先把组件引入一番再说
引入组件
组件的使用向来是先引入再说,毫不例外的,看下面的组件引用
// App.vue文件 -> js部分 <script> // 引入Swiper和SwiperItem组件 import Swiper from './components/Swiper/Swiper'; import SwiperItem from './components/Swiper/SwiperItem'; export default { // 组件内注册组件 components: { Swiper, SwiperItem } } </script> 复制代码
js部分已经将引入的组件注册到了当前App组件中,下面就开始在模板中使用它们
// App.vue文件 -> html部分 <template> <div id="app"> <Swiper v-model="currentId"> <SwiperItem :id="currentId"> <div>第一张图</div> </SwiperItem> <SwiperItem :id="currentId"> <div>第二张图</div> </SwiperItem> <SwiperItem :id="currentId"> <div>第三张图</div> </SwiperItem> </Swiper> </div> </template> 复制代码
小朋友,你是否有很多问号?别着急,且听风吟
Swiper组件里放了3个SwiperItem组件,而且SwiperItem组件里实现的内容是完全相同的
所以,没必要写3遍,直接用一个数组通过v-for
渲染出来就OK了
数据
这里我简单用node写了个接口,返回轮播图的数据,如果不想写个接口的话,我直接把数据贴出来,让大家直观的看到
mock数据
// 轮播图数据 const albums = [ { "id": 1, "title": "叶惠美", "public": 2003, "song": '晴天', "img": "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=281827478,1045705647&fm=26&gp=0.jpg"}, { "id": 2, "title": "七里香", "public": 2004, "song": '七里香', "img": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584873891083&di=7892d2142e6aba7e203d270e20599235&imgtype=0&src=http%3A%2F%2Fpic.rmb.bdstatic.com%2Fc1505303db7c257f248adc87b6e22fd5.jpeg"}, { "id": 3, "title": "十一月的萧邦", "public": 2005, "song": '夜曲', "img": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584873911041&di=51fc38d5805edc63fdd7301dbcef316f&imgtype=0&src=http%3A%2F%2Fzximg.ingping.com%2Fueditor%2Fjsp%2Fupload%2F201705%2F201705031153520358724.jpg"}, { "id": 4, "title": "依然范特西", "public": 2006, "song": '听妈妈的话', "img": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584873937432&di=ebf0092e78d5499f54728eeb43449414&imgtype=0&src=http%3A%2F%2Fimg1.dongqiudi.com%2Ffastdfs2%2FM00%2F66%2FBA%2FChOqM1rO_0mADp5rAACEhAjn7aY043.jpg"} ]; 复制代码
我写的接口是/getalbums
,返回的是json格式,{code: 0, data: albumes}
下面我还是按照正常的请求来操作,在之前的目录结构中,大家应该看到了api/shop.js
文件,这里是我封装好的用来请求轮播图数据的代码
// api/shop.js文件 import axios from 'axios'; // 拦截返回的响应数据 axios.interceptors.response.use(res => res.data); export default { async getAlbums() { let { data } = await axios.get('/api/getalbums'); return data; } } 复制代码
请求数据
现在让我们回到App.vue这里,开始进行请求操作,然后v-for
遍历数组并渲染SwiperItem组件
// App.vue文件 -> js部分 <script> import Swiper from './components/Swiper/Swiper'; import SwiperItem from './components/Swiper/SwiperItem'; // 引入封装好的请求方法 // @是webpack配的alias别名,指代src目录 import shop from '@/api/shop'; export default { data() { // 轮播图数据 sliders: [], // 当前id,用来显示对应的图片 currentId: 1 }, async mounted() { // 请求数据并更新slider数组 this.sliders = await shop.getAlbums(); }, components: { Swiper, SwiperItem } } </script> 复制代码
渲染数据
渲染数据的部分要交给我们的模板来处理了
// App.vue文件 -> html部分 <template> <div id="app"> <Swiper v-model="currentId" v-if="sliders.length"> <template v-for="item in sliders"> <SwiperItem :key="item.id" :id="item.id"> <img :src="item.img" /> </SwiperItem> </template> </Swiper> </div> </template> 复制代码
上面模板为什么写成这样?
v-model
也可以在组件上进行绑定- 一个组件上的
v-model
默认会利用名为value
的prop和名为input
的事件
- 一个组件上的
v-if
控制组件是在拿到数据后才进行渲染- SwiperItem上的
id
动态属性是用来区分当前应该展示哪张图片的重点
好了,写到这里,基本的展示效果已经搞定了,img
元素的宽高,在App.vue的css部分可以定义一下如:#app img { width: 100%; height: 220px; }
,这样限制了宽高看起来就比较合适了
现在,开始尽请的开发吧
Swiper组件
Swiper内部写了多个SwiperItem组件,所以需要一个slot
插槽来进行内容的分发,并且接收了v-model
绑定数据后传递过来的value
下面,来看一眼实现的逻辑吧
// Swiper.vue文件 <template> <div class="swiper"> <div class="view"> <slot></slot> </div> </div> </template> <script> export default { props: { // v-model默认传递过来的value属性 value: { type: Number, default: 1 } } } </script> <style scoped> .swiper { position: relative; width: 100%; height: 220px; overflow: hidden; } </style> 复制代码
先写了一个基本的逻辑,剩下的逻辑稍后再写,我们再把SwiperItem组件实现一下
SwiperItem组件
轮播图和选项卡很类似,都是当前只显示一张图,其余的都隐藏起来,SwiperItem组件就是用来做这些事情的
// SwiperItem.vue文件 <template> <transition name="items"> <div class="swiper-item" v-if="isShow"> <slot></slot> </div> </transition> </template> <script> export default { props: { id: { type: Number, // 必填属性 required: true } }, data() { return { selectedId: 1 } }, computed: { isShow() { // 根据props传过来的值判断v-if显示 return this.id === this.selectedId; } } } </script> <style scoped> .items-enter-active, .items-leave-active { transition: .5s linear; } .items-leave-to { transform: translateX(-100%); } .items-enter { transform: translateX(100%); } .items-enter-active { position: absolute; width: 100%; height: 100%; left: 0; top: 0; } </style> 复制代码
初始化轮播图
回到Swiper组件里,我们需要写一个方法用来展示轮播图,那么就简单的起名为show方法吧,
它的作用就是:遍历$children
子组件实例,修改子组件里的selectedId的值,然后显示初始轮播图
// Swiper.vue文件 <script> export default { ...省略, data() { return { index: 1 } }, methods: { show() { // 给index赋值为传递过来的value // 如果没有传递value值,就默认取第一个子组件里的id值 this.index = this.value || this.$children[0].id; // 遍历子组件,并修改实例上对应的selectedId值 this.$children.forEach(vm => { vm.selectedId = this.index; }); } }, mounted() { // 初始化 this.show(); } } </script> 复制代码
实现轮播图小点
轮播图下方常见会有对应的小点,让用户直观的看到一共有几张轮播图片,其实实现起来很easy,因为只要我们知道有几条数据就可以利用v-for
对应循环出来了
// Swiper.vue文件 <template> <div class="swiper"> <div class="view"> <slot></slot> </div> <div class="dots"> <span class="dot" v-for="dot in len" :key="dot" :class="{active: index === dot}"> </span> </div> </div> </template> <script> export default { ...省略, data() { return { index: 1, // 记录轮播图的图片数量 // 定义在data是为了在模板使用 len: 0 } }, methods: { ...省略 }, mounted() { // 初始化 this.show(); // 更新len的长度,其实就是有几个子组件而已 this.len = this.$children.length; } } </script> <style scoped> .swiper { position: relative; width: 100%; height: 220px; overflow: hidden; } /* 轮播图小点样式 */ .swiper .dots { position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); } .swiper .dots .dot { width: 10px; height: 4px; border-radius: 6px; background-color: rgba(255, 255, 255, 0.7); display: inline-block; margin: 0 2px; } .swiper .dots .active { width: 14px; background-color: #fff; } </style> 复制代码
经过上面的一番折腾,总该有点该有的样子了吧,让我们看看效果图
自动播放
很多轮播图效果都是自带自动播放的,所以我们当前也不会置之不理。我们通过给Swiper组件传递autoplay
的prop来进行设置
// App.vue文件 -> html部分 <template> <div id="app"> <Swiper v-model="currentId" v-if="sliders.length" :autoplay="true"> ...省略 </Swiper> </div> </template> 复制代码
父组件App传递了autoplay
了,那么接下来就继续回到我们的Swiper组件
我们再实现一个play
方法,主要是写一个定时器,然后再通过change
方法来修改value的值,做到自动播放
// Swiper.vue文件 -> js部分 <script> export default { props: { ...省略, autoplay: { type: Boolean, default: false } }, data() { return { index: 1, len: 0 } }, methods: { show() {...省略}, // 定时器播放 play() { if (this.autoplay) { this.timer = setInterval(() => { this.change(this.index + 1); }, 3000); } }, // 切换轮播图 change(index) { // 处理右边界如果超过了len if (index === this.len + 1) { // 将index改回到1 index = 1; } else if (index === 0) { index = this.len; } // 通过$emit,触发input事件,修改value为index值 this.$emit('input', index); }, // 清除定时器 stop() { clearInterval(this.timer); this.timer = null; } }, watch: { // 这里是关键 // 监听value值的变化,value赋给了index,随着每次的index变化 // 就重新调用一下show方法,刷新视图 value() { this.show(); } }, mounted() { this.show(); this.len = this.$children.length; // 调用自动播放 this.play(); } } </script> 复制代码
一起来看看效果吧
左右切换
默认自动播放的时候,图片都是向左移动到-100%的位置。如果考虑到用户切换的话,需要先在mounted
时记录一下前一个索引值prevIndex
前一个索引值prevIndex
和当前的索引值index
去比较,如果大于了当前的index
,那么就说明是反向轮播,图片需要向右移动
让我们再回到SwiperItem组件,给它添加一个动态class,用来显示正反方向的过渡效果
不同方向过渡
// SwiperItem.vue文件 <template> <transition name="items"> <div class="swiper-item" v-if="isShow" :class="{direction}"> <slot></slot> </div> </transition> </template> <script> export default { ...省略 data() { return { index: 1, // 判断方向 direction: false } } } </script> <style scoped> ...省略 /* 反向过渡 */ .items-leave-to.direction { transform: translateX(100%); } .items-enter.direction { transform: translateX(-100%); } </style> 复制代码
判断方向
上面的代码为我们修改了不同方向的过渡效果,废话不再多说,直接看代码吧
// Swiper.vue文件 -> js部分 <script> export default { ...省略, methods: { show() { this.index = this.value || this.$children[0].id; this.$children.forEach(vm => { this.$nextTick(() => { vm.selectedId = this.index; }); // 如果prevIndex大于了index就是反向轮播 vm.direction = this.prevIndex > this.index; // 处理自动播放时的边界情况 if (this.timer) { if (this.prevIndex === 1 && this.index === this.len) { // 处理从第1张反向跳到最后一张的条件 vm.direction = true; } else if (this.prevIndex === this.len && this.index === 1) { // 处理最后一张正向跳到第一张的条件 vm.direction = false; } } }); }, change(index) { this.prevIndex = this.index; ...省略 } }, mounted() { this.show(); this.len = this.$children.length; this.play(); // 先记录当前的值 this.prevIndex = this.index; } } </script> 复制代码
简单梳理:
- 修改show方法
prevIndex
最开始是undefined,因为在mounted
按照顺序调用show
方法的时候,prevIndex
还没有赋值为index
- 相当于
prevIndex > index
第一次比较的时候,是undefined和1在比较,之后在每次切换图片,调用change
方法的时候又给prevIndex
赋了index
的值 $nextTick
是为了在vm.direction
数据被修改了,等待DOM更新后再修改对应的selectedId
以对应展示对应图片
- 修改change方法
- change方法修改的地方很少,
this.prevIndex = this.index
就是在每次切换的时候把上一次的index
赋给了prevIndex
- 比如: 最开始
prevIndex
为undefined,index
为1;当prevIndex
为1的时候,index
为2,以此类推的赋值,这样大家就能够理解了吧
至此,我们把左右切换的过渡效果代码逻辑搞定了,还差最后一步了,让我们给轮播图添加touch
事件,让用户可以左右切换图片
添加touch事件
上面已经完成了判断方向的逻辑,用户左右切换简直是小菜一碟了,首先要给元素上添加touch事件
// Swiper.vue文件 <template> <div class="swiper"> <div class="view" @touchstart="touchstart" @touchend="touchend"> <slot></slot> </div> ...省略 </div> </template> <script> export default { ...省略, methods: { touchstart(e) { // 刚触摸屏幕时记录一个x坐标 this.startX = e.touches[0].pageX; // 并且停止自动播放 this.stop(); }, touchend(e) { // 计算开始点和手指抬起时离开点的坐标差值 let disX = e.changedTouches[0].pageX - this.startX; // 如果小于0就表示是正向切换(向左滑动) // 反之,就是反向切换(向右滑动) if (disX < 0) { this.change(this.index + 1); } else { this.change(this.index - 1); } // 切换完毕后,继续进行自动播放 this.play(); }, ...省略 } } </script> 复制代码
写到这里就算完成了,让我们再来一起看看效果吧
最后的最后:由于组件里用到了定时器,那么别忘记了在beforeDestroy
的时候,调用stop
方法清除一下定时器
学无止境
本来还想写两个别的可复用组件,不过怕字数太多,大家看的疲劳了,就点到为止吧,我也会把相应的地址发出来,让大家方便参考
拾人牙慧,学无止境,感谢大家观看了
这篇关于编写可复用的组件,我们可以收获很多的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-05小米13T Pro系统合集:性能与摄影的极致融合,值得你升级的系统ROM
- 2024-10-01基于Python+Vue开发的医院门诊预约挂号系统
- 2024-10-01基于Python+Vue开发的旅游景区管理系统
- 2024-10-01RestfulAPI入门指南:打造简单易懂的API接口
- 2024-10-01初学者指南:了解和使用Server Action
- 2024-10-01Server Component入门指南:搭建与配置详解
- 2024-10-01React 中使用 useRequest 实现数据请求
- 2024-10-01使用 golang 将ETH账户的资产平均分散到其他账户
- 2024-10-01JWT用户校验课程:从入门到实践
- 2024-10-01Server Component课程入门指南