【Vue3 组件封装】vue3 轮播图组件封装

简介: 【Vue3 组件封装】vue3 轮播图组件封装

轮播图功能-获取数据

目标: 基于pinia获取轮播图数据

核心代码:

(1)在types/data.d.ts文件中定义轮播图数据的类型声明

// 所有接口的通用类型
export type ApiRes <T> = {
    code: string,
    msg: string,
    result: T
}
// 轮播图类型
export type BannerItem = {
  hrefUrl: string
  id: string
  imgUrl: string
  type: string
}

(2)在store/home.ts文件中封装接口,用于获取轮播图数据

import { ApiRes, BannerItem } from '@/types/data'
import request from '@/utils/request'
import { defineStore } from 'pinia'
export default defineStore('home', {
  state: () => ({
    bannerList: [] as BannerItem[],
  }),
  actions: {
    async getBannerList() {
      const {data: res} = await request.get<ApiRes<BannerItem[]>>('/home/banner')
      this.bannerList = res.result
    },
  },
})

(3)在store/index.ts中导入

import useCategoryStore from './modules/category'
import useHomeStore from './modules/home'
export default function useStore() {
  return {
    category: useCategoryStore(),
    home: useHomeStore(),
  }
}

(4)通过开发者工具查看数据

<script lang="ts" setup>
import useStore from '@/store'
const { home } = useStore()
home.getBannerList()
</script>

轮播图-通用轮播图组件

项目中会多次使用到轮播图组件,但是轮播图渲染的数据是不一样的。

但是轮播图的基本功能都是一样的,比如图片切换,自动播放等等。

因此需要封装一个通用的轮播图组件。

(1)通用轮播图的基本结构src/components/carousel/index.vue

fade 类:用于控制图片的显示和隐藏

active 类:用于控制小圆点高亮

<script lang="ts" setup name="Carousel">
defineProps()
</script>
<template>
  <div class="carousel">
    <ul class="carousel-body">
      <li class="carousel-item fade">
        <RouterLink to="/">
          <img
            src="http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/1ba86bcc-ae71-42a3-bc3e-37b662f7f07e.jpg"
            alt=""
          />
        </RouterLink>
      </li>
      <li class="carousel-item">
        <RouterLink to="/">
          <img
            src="http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/1ba86bcc-ae71-42a3-bc3e-37b662f7f07e.jpg"
            alt=""
          />
        </RouterLink>
      </li>
      <li class="carousel-item">
        <RouterLink to="/">
          <img
            src="http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/1ba86bcc-ae71-42a3-bc3e-37b662f7f07e.jpg"
            alt=""
          />
        </RouterLink>
      </li>
    </ul>
    <a href="javascript:;" class="carousel-btn prev"
      ><i class="iconfont icon-angle-left"></i
    ></a>
    <a href="javascript:;" class="carousel-btn next"
      ><i class="iconfont icon-angle-right"></i
    ></a>
    <div class="carousel-indicator">
      <span class="active"></span>
      <span></span>
      <span></span>
      <span></span>
      <span></span>
    </div>
  </div>
</template>
<style scoped lang="less">
.xtxcarousel {
  width: 100%;
  height: 100%;
  min-width: 300px;
  min-height: 150px;
  position: relative;
  .carousel {
    &-body {
      width: 100%;
      height: 100%;
    }
    &-item {
      width: 100%;
      height: 100%;
      position: absolute;
      left: 0;
      top: 0;
      opacity: 0;
      transition: opacity 0.5s linear;
      &.fade {
        opacity: 1;
        z-index: 1;
      }
      img {
        width: 100%;
        height: 100%;
      }
    }
    &-indicator {
      position: absolute;
      left: 0;
      bottom: 20px;
      z-index: 2;
      width: 100%;
      text-align: center;
      span {
        display: inline-block;
        width: 12px;
        height: 12px;
        background: rgba(0, 0, 0, 0.2);
        border-radius: 50%;
        cursor: pointer;
        ~ span {
          margin-left: 12px;
        }
        &.active {
          background: #fff;
        }
      }
    }
    &-btn {
      width: 44px;
      height: 44px;
      background: rgba(0, 0, 0, 0.2);
      color: #fff;
      border-radius: 50%;
      position: absolute;
      top: 228px;
      z-index: 2;
      text-align: center;
      line-height: 44px;
      opacity: 0;
      transition: all 0.5s;
      &.prev {
        left: 20px;
      }
      &.next {
        right: 20px;
      }
    }
  }
  &:hover {
    .carousel-btn {
      opacity: 1;
    }
  }
}
</style>

(2)全局注册通用轮播图 src/components/index.ts

import type { App } from 'vue'
import skelecton from './skeleton/index.vue'
+import Carousel from './carousel/index.vue'
export default {
  install(app: App) {
    app.component(skelecton.name, skelecton)
+    app.component(Carousel.name, Carousel)
  },
}

(3)在广告组件中使用src/views/home/components/home-banner.vue

<template>
  <div class="home-banner">
    <!-- 轮播图 -->
    <Carousel></XtxCarousel>
  </div>
</template>

(4)覆盖样式,控制箭头和小圆点的位置src/views/home/components/home-banner.vue

:deep(.carousel-btn.prev) {
  left: 270px!important;
}
:deep(.carousel-indicator) {
  padding-left: 250px;
}

(5)查看效果

轮播图-数据渲染

目的

home-banner组件把数据传递给Carousel组件进行渲染

(1)父传子的方式将数据传给通用轮播图组件src/views/home/components/home-banner.vue

<Carousel :slides="home.bannerList"></Carousel>

(2)子组件接收数据src/components/carousel/index.vue

了解写法:如果通过js的方法定义类型,需要单独引入PropType进行编写

<script lang="ts" setup name="Carousel">
import { BannerItem } from '@/types/data'
// import { PropType } from 'vue'
// defineProps({
//   slides: {
//     type: Array as PropType<BannerItem[]>,
//     required: true,
//   },
// })
defineProps<{
  slides: BannerItem[]
}>()
</script>

(3)渲染轮播图数据src/components/carousel/index.vue

<template>
  <div class="carousel">
    <ul class="carousel-body">
      <li class="carousel-item fade" v-for="item in slides" :key="item.id">
        <RouterLink :to="item.hrefUrl">
          <img :src="item.imgUrl" alt="" />
        </RouterLink>
      </li>
    </ul>
    <a href="javascript:;" class="carousel-btn prev">
      <i class="iconfont icon-angle-left"></i>
    </a>
    <a href="javascript:;" class="carousel-btn next">
      <i class="iconfont icon-angle-right"></i>
    </a>
    <div class="carousel-indicator">
      <span v-for="item in slides" :key="item.id" class="active"></span>
    </div>
  </div>
</template>

(4)控制高亮的下标

<script lang="ts" setup name="Carousel">
const active = ref(0)
</script>

(5)高亮渲染

  1. 添加的fade的图片才会展示,所以根据当前索引号进行判断,索引号等于active的才进行展示
  2. 添加了active类名的小圆点才会高亮,高亮逻辑跟图片一致
<template>
  <div class="carousel">
    <ul class="carousel-body">
      <li
        class="carousel-item"
+        :class="{ fade: active === index }"
+        v-for="(item, index) in slides"
        :key="item.id"
      >
        <RouterLink :to="item.hrefUrl">
          <img :src="item.imgUrl" alt="" />
        </RouterLink>
      </li>
    </ul>
    <a href="javascript:;" class="carousel-btn prev">
      <i class="iconfont icon-angle-left"></i>
    </a>
    <a href="javascript:;" class="carousel-btn next">
      <i class="iconfont icon-angle-right"></i>
    </a>
    <div class="carousel-indicator">
      <span
+        v-for="(item, index) in slides"
        :key="item.id"
+        :class="{ active: active === index }"
      ></span>
    </div>
  </div>
</template>

轮播图-逻辑封装

实现需求:

  1. 轮播图里面的图片需要从父组件传入(因为轮播组件可以复用)
  2. 父组件需要控制轮播图的是否自动播放、动画时间(处理默认值逻辑)
  • 是否自动播放和动画时间都是需要默认值的(如果不传就可以使用轮播组件自己提供的默认值)
  1. 播放逻辑
  1. 点击小圆点可以切换图片
  2. 点击prev和next按钮可以播放指定图片(根据图片个数判断播放的循环)
  3. 如果父组件配置了自动播放,则需要定时播放图片
  4. 鼠标进入轮播图,暂停轮播
  5. 鼠标离开轮播图,继续轮播
  6. 注意点:组件卸载的时候需要清除定时轮播效果(不然组件重新加载的时候会导致多个定时器开启)

(1)父组件传值给轮播图src/views/home/components/home-banner.vue

<template>
  <div class="home-banner">
    <!-- 轮播图 -->
    <Carousel :slides="slides" autoPlay :duration="3000"></XtxCarousel>
  </div>
</template>

(2)props接收src/components/Carousel.vue

<script lang="ts" setup name="Carousel">
import { BannerItem } from '@/types/data'
import { ref, PropType } from 'vue'
defineProps({
  slides: {
    type: Array as PropType<BannerItem[]>,
    required: true,
  },
  autoPlay: {
    type: Boolean,
    default: false,
  },
  duration: {
    type: Number,
    default: 3000,
  },
})
const active = ref(0)
</script>

(3)轮播图的播放逻辑

<script lang="ts" setup name="Carousel">
import { BannerItem } from '@/types/data'
import { onMounted, onUnmounted, PropType, ref } from 'vue'
// import { PropType } from 'vue'
const props = defineProps({
  slides: {
    type: Array as PropType<BannerItem[]>,
    required: true,
  },
  duration: {
    type: Number,
    default: 3000,
  },
  autoPlay: {
    type: Boolean,
    default: false,
  },
})
// const props = defineProps<{
//   slides: BannerItem[]
// }>()
// 控制高亮
const active = ref(0)
const prev = () => {
  if (active.value <= 0) {
    active.value = props.slides.length - 1
  } else {
    active.value--
  }
}
const next = () => {
  if (active.value >= props.slides.length - 1) {
    active.value = 0
  } else {
    active.value++
  }
}
const play = () => {
  // 如果没有自动播放
  if (!props.autoPlay) return
  // 在ts中,使用定时器,window.setInterval
  timer = window.setInterval(() => {
    next()
  }, props.duration)
}
const stop = () => {
  clearInterval(timer)
}
let timer = -1
// 自动播放
onMounted(() => {
  play()
})
onUnmounted(() => {
  stop()
})
</script>

(4)鼠标进入和离开操作

<div class="carousel" @mouseenter="stop" @mouseleave="play">

(5)鼠标经过小圆点切换

<span
  v-for="(item, index) in slides"
  :key="item.id"
  :class="{ active: active === index }"
  @mouseenter="active = index"
></span>

(6)点击左右箭头切换

const prev = () => {
  if (active.value === 0) {
    active.value = props.slides.length - 1
  } else {
    active.value--
  }
}
const next = () => {
  if (active.value === props.slides.length - 1) {
    active.value = 0
  } else {
    active.value++
  }
}
// 注册事件
<a href="javascript:;" class="carousel-btn prev" @click="prev">
  <i class="iconfont icon-angle-left"></i>
</a>
<a href="javascript:;" class="carousel-btn next" @click="next">
  <i class="iconfont icon-angle-right"></i>
</a>
vascript
const prev = () => {
  if (active.value === 0) {
    active.value = props.slides.length - 1
  } else {
    active.value--
  }
}
const next = () => {
  if (active.value === props.slides.length - 1) {
    active.value = 0
  } else {
    active.value++
  }
}
// 注册事件
<a href="javascript:;" class="carousel-btn prev" @click="prev">
  <i class="iconfont icon-angle-left"></i>
</a>
<a href="javascript:;" class="carousel-btn next" @click="next">
  <i class="iconfont icon-angle-right"></i>
</a>
目录
相关文章
|
2月前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
147 64
|
13天前
|
JavaScript API 数据处理
vue3使用pinia中的actions,需要调用接口的话
通过上述步骤,您可以在Vue 3中使用Pinia和actions来管理状态并调用API接口。Pinia的简洁设计使得状态管理和异步操作更加直观和易于维护。无论是安装配置、创建Store还是在组件中使用Store,都能轻松实现高效的状态管理和数据处理。
45 3
|
2月前
|
前端开发 JavaScript 测试技术
Vue3中v-model在处理自定义组件双向数据绑定时,如何避免循环引用?
Web 组件化是一种有效的开发方法,可以提高项目的质量、效率和可维护性。在实际项目中,要结合项目的具体情况,合理应用 Web 组件化的理念和技术,实现项目的成功实施和交付。通过不断地探索和实践,将 Web 组件化的优势充分发挥出来,为前端开发领域的发展做出贡献。
41 8
|
2月前
|
存储 JavaScript 数据管理
除了provide/inject,Vue3中还有哪些方式可以避免v-model的循环引用?
需要注意的是,在实际开发中,应根据具体的项目需求和组件结构来选择合适的方式来避免`v-model`的循环引用。同时,要综合考虑代码的可读性、可维护性和性能等因素,以确保系统的稳定和高效运行。
34 1
|
2月前
|
JavaScript
Vue3中使用provide/inject来避免v-model的循环引用
`provide`和`inject`是 Vue 3 中非常有用的特性,在处理一些复杂的组件间通信问题时,可以提供一种灵活的解决方案。通过合理使用它们,可以帮助我们更好地避免`v-model`的循环引用问题,提高代码的质量和可维护性。
45 1
|
7天前
|
JavaScript
vue使用iconfont图标
vue使用iconfont图标
51 1
|
17天前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
2月前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱前端的大一学生,专注于JavaScript与Vue,正向全栈进发。博客分享Vue学习心得、命令式与声明式编程对比、列表展示及计数器案例等。关注我,持续更新中!🎉🎉🎉
48 1
vue学习第一章
|
2月前
|
JavaScript 前端开发 索引
vue学习第三章
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中的v-bind指令,包括基本使用、动态绑定class及style等,希望能为你的前端学习之路提供帮助。持续关注,更多精彩内容即将呈现!🎉🎉🎉
34 1
|
2月前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
41 1
vue学习第四章

热门文章

最新文章