手搓vue3组件_1.封装一个button

13 篇文章 0 订阅

我的icepro参考地址,内有参考代码,有条件的割割点点star

实现要求:

  • 基于vue3
  • 支持通过colors(更改颜色)
  • 支持点击事件
  • …支持其他的自定义样式(例如圆角,size等等)

最基础的第一步:

父组件引入并使用:

<template>
  <div class="buttonLim">
    我的按钮:
    <ice-button>
      primary
    </ice-button>
  </div>
</template>
<script setup>
import IceButton from '../../components/other/ice-button.vue'
</script>
<style scoped lang="less">
</style>

子组件中使用slot去展示:

<template>
  <div class="ice-button">
    <slot></slot>
  </div>
</template>
<script setup>
</script>
<style scoped lang="less">
</style>

run:

在这里插入图片描述

那么,把它的样式改的好看一些:

父组件:

<template>
  <div class="buttonLim">
    我的按钮:
    <ice-button>
      primary
    </ice-button>

  </div>
</template>

<script setup>
import IceButton from '../../components/other/ice-button.vue'</script>

<style scoped lang="less">
.buttonLim {
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
}
</style>

子组件:

<template>
  <div class="ice-button">
    <slot></slot>
  </div>
</template>

<script setup>

</script>

<style scoped lang="less">
.ice-button {
  border-radius: .3rem;
  border: rgba(0, 0, 0, .7) 1px solid;
  background: rgba(0, 0, 0, .2);
  width: fit-content;
  padding: .2rem .4rem;
  margin: .1rem .2rem;
  user-select: none;
}

</style>

在这里插入图片描述
当然,此时他的颜色并不够好看,那么如果想通过props向子组件自定义颜色:
子组件:

<template>
  <div class="ice-button">
    <slot></slot>
  </div>
</template>

<script setup>
const props = defineProps({
  color: {
    type: String,
    default: ''
  }
})
</script>

这样你传过来了,但是想怎么用呢,

这里要求颜色有未hover时的颜色和hover时的颜色,hover时的颜色自动计算出来

而此时可以考虑使用到css的变量了,像是:
子组件:

<template>
  <div class="ice-button"
       :class="[
           color?'hoverColor':'defaultColor'
       ]"
       :style="{ '--color': color,'--hover-color': hoverColor(color) }"
  >
    <slot></slot>
  </div>
</template>

<script setup>
const props = defineProps({
  color: {
    type: String,
    default: ''
  }
})
const hoverColor = (rgb) => {
  return rgb.replaceAll(')', ',.5)')
}
</script>

<style scoped lang="less">
.ice-button {
  border-radius: .3rem;
  width: fit-content;
  padding: .2rem .4rem;
  margin: .1rem .2rem;
  user-select: none;
  transition-duration: .3s;
}

.defaultColor {
  border: rgba(0, 0, 0, .7) 1px solid;
  background: rgba(0, 0, 0, .2);
}

.hoverColor {
  color: var(--color);
  border: var(--color) 1px solid;

  &:hover {
    color: var(--hover-color);
    border: var(--hover-color) 1px solid;
  }
}
</style>

父组件的调用:

<template>
  <div class="buttonLim">
    我的按钮:
    <ice-button color="rgb(251, 139, 5)">
      primary
    </ice-button>
    <ice-button color="rgb(234, 137, 88)">
      primary
    </ice-button>

  </div>
</template>

<script setup>
import IceButton from '../../components/other/ice-button.vue'</script>

<style scoped lang="less">
.buttonLim {
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
}
</style>

run:
在这里插入图片描述

解释一下:

子组件中,如果传入了color的值,那么子组件的类名hoverColor生效,反之defaultColor生效,这里是给class传入了一个数组,如果你查看elementui的源码,会发现他们也是这样实现组件的type的切换,用过了才知道这个技巧是如此好用

还有,这里只是传入了一个rgb的值,然后在子组件中自动计算出来另一个颜色值(直接改为rgba,opacity为0.5)

支持点击事件

如果你直接使用下面的方式来绑定:
父组件:

<template>
  <div class="buttonLim">
    我的按钮:
    <ice-button color="rgb(251, 139, 5)">
      primary
    </ice-button>
    <ice-button color="rgb(234, 137, 88)">
      primary
    </ice-button>

    <ice-button @click="clickTrigger" color="rgb(242, 72, 27)" ref="btn">
      click
    </ice-button>


  </div>
</template>

<script setup>
import IceButton from '../../components/other/ice-button.vue'
import { ref } from 'vue'

const btn = ref()
const clickTrigger = async () => {
  console.log('clickTrigger--->')
  const str = '我即将要赋值的文字'
  if (await copyText(str)) {
    console.log('success')
  } else {
    console.log('error')
  }
}

const copyText = function (str) {
  return navigator.clipboard.writeText(str)
      .then(() => {
        return true
      })
      .catch(() => {
        return false
      })
}

</script>

<style scoped lang="less">
.buttonLim {
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
}
</style>

子组件:

<template>
  <div class="ice-button"
       :class="[
           color?'hoverColor':'defaultColor'
       ]"
       :style="{ '--color': color,'--hover-color': hoverColor(color) }"
  >
    <slot></slot>
  </div>
</template>

<script setup>
const props = defineProps({
  color: {
    type: String,
    default: ''
  }
})
const hoverColor = (rgb) => {
  return rgb.replaceAll(')', ',.5)')
}
</script>

<style scoped lang="less">
.ice-button {
  border-radius: .3rem;
  width: fit-content;
  padding: .2rem .4rem;
  margin: .1rem .2rem;
  user-select: none;
  transition-duration: .3s;
}

.defaultColor {
  border: rgba(0, 0, 0, .7) 1px solid;
  background: rgba(0, 0, 0, .2);
}

.hoverColor {
  color: var(--color);
  border: var(--color) 1px solid;

  &:hover {
    color: var(--hover-color);
    border: var(--hover-color) 1px solid;
  }
}
</style>

这样没问题可以,但是有时会报错,click不是原生事件,这里我没有复现,淡然,你也可以在复习bug的时候想起这篇文章

这里的逻辑是点击左侧的item,赋值文字,但是这里的子组件没有定义click的处理事件,上面的button也是,可能会报这种错,

  • 如何解决:

在子组件中定义click事件:
子组件:

<template>
  <div class="ice-button"
       @click="clickCallBack"
       :class="[
           color?'hoverColor':'defaultColor'
       ]"
       :style="{ '--color': color,'--hover-color': hoverColor(color) }"
  >
    <slot></slot>
  </div>
</template>

<script setup>
const props = defineProps({
  color: {
    type: String,
    default: ''
  }
})
const hoverColor = (rgb) => {
  return rgb.replaceAll(')', ',.5)')
}

const emit = defineEmits(['click'])
const clickCallBack = (evt) => {

  emit('click', evt)
}
</script>

<style scoped lang="less">
.ice-button {
  border-radius: .3rem;
  width: fit-content;
  padding: .2rem .4rem;
  margin: .1rem .2rem;
  user-select: none;
  transition-duration: .3s;
}

.defaultColor {
  border: rgba(0, 0, 0, .7) 1px solid;
  background: rgba(0, 0, 0, .2);
}

.hoverColor {
  color: var(--color);
  border: var(--color) 1px solid;

  &:hover {
    color: var(--hover-color);
    border: var(--hover-color) 1px solid;
  }
}
</style>

这里的clickCallBack接收并emit一下click事件
emit函数会触发父组件绑定的click事件。当用户点击按钮时,父组件会接收到这个事件,并执行相应的操作。

自定义圆角

这里其实还是使用props来自定义圆角,例如我实现下面几个(round和block)按钮:
在这里插入图片描述
父组件的调用:

    自定义圆角:
    <ice-button round>round</ice-button>
    <ice-button block>block</ice-button>

子组件:

<template>
  <div class="ice-button"
       @click="clickCallBack"
       :class="[
           color?'hoverColor':'defaultColor',
           round?'round':'',
           block?'block':''
       ]"
       :style="{ '--color': color,'--hover-color': hoverColor(color) }"
  >
    <slot></slot>
  </div>
</template>

<script setup>
const props = defineProps({
  color: {
    type: String,
    default: ''
  },
  round: {
    type: Boolean,
    default: false
  },
  block: {
    type: Boolean,
    default: false
  }
})
const hoverColor = (rgb) => {
  return rgb.replaceAll(')', ',.5)')
}

const emit = defineEmits(['click'])
const clickCallBack = (evt) => {
  emit('click', evt)
}
</script>

<style scoped lang="less">
.ice-button {
  border-radius: .3rem;
  width: fit-content;
  padding: .2rem .4rem;
  margin: .1rem .2rem;
  user-select: none;
  transition-duration: .3s;
}

.defaultColor {
  border: rgba(0, 0, 0, .7) 1px solid;
  color: rgba(0, 0, 0, .7);
  transition-duration: .3s;

  &:hover {
    color: rgba(0, 0, 0, .4);
    border: rgba(0, 0, 0, .4) 1px solid;
  }
}

.hoverColor {
  color: var(--color);
  border: var(--color) 1px solid;

  &:hover {
    color: var(--hover-color);
    border: var(--hover-color) 1px solid;
  }
}

.round {
  border-radius: 2rem;
}

.block {
  border-radius: 0;
}
</style>

当然,也可以混合使用:
在这里插入图片描述

    <ice-button block color="rgb(242, 72, 27)">混合</ice-button>

以上说的功能能都实现了

注意这里的代码还有很多没有优化,颜色获取,其他自定义type之类的都没有处理,关于更多的细节优化,详见icepro

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无名之辈无名之辈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值