组件封装

127 阅读2分钟

如果你有梦想,就要守护它。 --当幸福来敲门

什么是组件

对功能、ui样式的封装,一个功能或者一个ui样式就是一个组件,导航栏,banner,页脚等等这些功能、样式都是组件

按组件注册方式分类:vue将组件分为全局组件和局部组件

什么时候需要封装

如果一块内容在项目中出现了两次就要考虑是否应进行封装
一个组件、一个函数、一个css 只要是需要多次使用的都可以考虑封装

如何封装一个组件呢?

1.我们在写代码的时候都会把封装的组件,或者公共样式放在一个统一的文件夹里面

文件夹2.jpg

2.进行全局注册或者局部注册
全局注册: 在components 下的index 进行全局注册
之后在global.d.ts文件里进行泛型约束

文件2.jpg

3.在需要使用的地方使用就OK啦

介绍几个我自己封装的小组件吧!

一 . 轮播图组件

1.轮播图播放逻辑

<script lang="ts" setup name="XtxCarousel">
    // BannerItem 轮播图类型
    import { BannerItem } from '@/types/data'
    import { onBeforeUnmount, onMounted, ref } from 'vue';
    // 接收父组件传来的数据
    const { slides, autoPlay = true ,duration=1 } = defineProps<{
      slides:BannerItem[]
      autoPlay?:boolean
      duration?:number
    }>()
    const active = ref(0)
    // 点击右箭头换轮播图
    const zuo = () =>{
      active.value --
      if(active.value < 0){
        active.value = slides.length-1
      }
    }
    // 点击左箭头换轮播图
    const you = () =>{
      active.value ++
      if(active.value === slides.length){
        active.value = 0
      }
    }

    // 组件挂载后,自动播放
    onMounted(()=>{
      start()
    })
    // 组件卸载之前执行的函数
      //  组件结束--停止播放
    onBeforeUnmount(()=>{
      stop()
    })

    let time = -1
      // 如果开启了自动播放,则每隔 duration 去播放下一个 hNext()

      // 鼠标离开要继续播放
    const start = () =>{
      if(autoPlay){
        // 开启定时器
        time = window.setInterval(()=>{
          // 播放
          you()
        },duration)
      }
    }
    // 鼠标hover要暂停播放
    const stop = () =>{
      clearInterval(time)
    }
</script>

// 样式
<template>
        <!-- @mouseleave="start"  鼠标移入  -->
        <!-- @mouseenter="stop"  鼠标移出 -->
     <div class="xtx-carousel"  @mouseleave="start" @mouseenter="stop">
        <ul class="carousel-body">
          <li 
          class="carousel-item" 
          :class="{fade: active === index }"
          v-for="(item,index) in slides " :key="item.id">
            <RouterLink to="/">
              <img
                :src="item.imgUrl"
                alt=""
              />
            </RouterLink>
          </li>
        </ul>
        <a href="javascript:;" class="carousel-btn prev" @click="zuo"><i class="iconfont icon-angle-left"></i></a>
        <a href="javascript:;" class="carousel-btn next"  @click="you"><i class="iconfont icon-angle-right"></i></a>
        <div class="carousel-indicator">
          <span
          v-for="( it,ind ) in slides " :key="it.id" 
          :class="{active:active === ind }"></span>
        </div>
      </div>
    </template>

    <style scoped lang="less">
    .xtx-carousel {
      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.类型 (根据自己的项目写自己的类型哦)

// 轮播图类型
export type BannerItem = {
    hrefUrl: string
    id: string
    imgUrl: string
    type: string
  }

3.全局注册

import type { App } from 'vue' 
import carousel from './XtXCarousel/XtXCarousel.vue'
export default {
    install(app: App) { 
         app.component(carousel.name, XtXCarousel)
    }
  }

4.使用组件

 <!-- 父传子,将数据传给组件 -->
   <!-- 自动播放 autoPlay 切换间隔时间 :duration="3000" -->
     <XtxCarousel :slides="home.bannerList"  autoPlay :duration="1000"></XtxCarousel>

二 .消息组件

1.先创建个组件
<!-- 消息提示组件 -->
<script lang="ts" setup name="XtxMessage">
// 引入 类型
import { MessageType } from './type';

defineProps<{type:'success' | 'error' | 'warning'}>()
defineProps<{
  type:MessageType,
  text:string
  } >()

// 定义一个对象,包含三种情况的样式,对象key就是类型字符串
三种提示情况
const style = {
  warning: {
    icon: 'icon-warning',
    color: '#E6A23C',
    backgroundColor: 'rgb(253, 246, 236)',
    borderColor: 'rgb(250, 236, 216)',
  },
  error: {
    icon: 'icon-shanchu',
    color: '#F56C6C',
    backgroundColor: 'rgb(254, 240, 240)',
    borderColor: 'rgb(253, 226, 226)',
  },
  success: {
    icon: 'icon-queren2',
    color: '#67C23A',
    backgroundColor: 'rgb(240, 249, 235)',
    borderColor: 'rgb(225, 243, 216)',
  },
}
</script>
 
<template>
  <div class="xtx-message" :style="style[type]">
    <i class="iconfont" :class="style[type].icon"></i>
    <span class="text"><slot></slot></span>
    // 提示文字 由插槽传过来
    <span class="text">{{text}}</span>
  </div>
</template>
2.定义写类型
export type MessageType = 'success' | 'error' | 'warning'
export type Message = { 
        type: MessageType 
        text: string 
}
3.通过代码的方式去渲染 创建的组件vue组件

创建一个TS文件在组件旁

// 导入XtxMessage.vue组件
// 通过代码的方式去渲染它
import { h, render } from 'vue'
import { Message } from './type'
import XtxMessage from './XtxMessage.vue'

// 创建一个dom容器
// 把这个容器添加在body上
const div = document.createElement('div')
div.className = "xtx-message-container"
document.body.appendChild(div)


let time = -1
// 调用这个函数
  // h的作用是: 产生虚拟DOM
  // render 的作用是将虚拟的DOM渲染成真实的DOM中
export default function Message(obj: Message){
  const vNode = h(XtxMessage, { type: obj.type, text: obj.text})
  // 把虚拟dom放入上面定义的容器中
  render(vNode, div)
  // 清除定时器
  clearTimeout(time)
  time = window.setTimeout(()=>{
    render(null,div)
  },obj.duration||3000)
}

Message.success =(value:string)=>{{
  Message({ type:"success",text:value , duration: 1500})
}}
Message.error =(value:string)=>{{
  Message({ type:"error",text:value , duration: 1500})
}}
Message.warning =(value:string)=>{{
  Message({ type:"warning",text:value , duration: 1500})
}}

4.使用
<script setup lang="ts">
  import Message from '@/components/XtxMessage'
      //使用
            Message.success('我还好')
            Message.error('我还好')
            Message.warning('我还好')

            //  Message({
            //  type:'success',
            //  text:'你好吗'
            // })

</script>

<template>
 <div id="div"></div>

</template>

三 .单选框

1.创建组件
<script lang="ts" setup name="XtxCheckbox">

  const { modelValue } = defineProps<{
    modelValue:boolean
  }>()
  // 通知父组件 ,改变状态,取反
  const emit = defineEmits<{
    (e: 'update:modelValue', val: boolean): void
  }>()
//  点击进行取反
const add = () =>{
    emit('update:modelValue', !modelValue)
  }
// 原版
//   const add = () =>{
//     emit('update:modelValue', false)
//   }
//  const addList = () =>{
//     emit('update:modelValue', true)
//   }
</script>

<template>
  <div class="xtx-checkbox">
    <!-- 选中时,要显示的图标 -->
    <i @click="add" v-if="modelValue" class="iconfont icon-checked"></i>
    <!-- 未选中时,要显示的图标 -->
    <i @click="add" v-else class="iconfont icon-unchecked"></i>
    <span> <slot/> </span>
  </div>
</template>

<style scoped lang="less">
.xtx-checkbox {
  display: inline-block;
  margin-right: 2px;
  .icon-checked {
    color: @xtxColor;
    ~ span {
      color: @xtxColor;
    }
  }
  i {
    position: relative;
    top: 1px;
  }
  span {
    margin-left: 2px;
  }
}
</style>
2.注册全局组件
前面轮播图有就不写了
3.使用
<!-- 确认框 -->
<XtxCheckbox v-model="isAgree">我已同意</XtxCheckbox>

告诉你一个万能的方法

当然了不会封装也没事

这影响我单手开法拉利吗 image.png