草庐IT

从0搭建Vue3组件库:button组件

迪迪滴 2023-03-28 原文

button组件几乎是每个组件库都有的;其实实现一个button组件是很简单的。本篇文章将带你一步一步的实现一个button组件。如果你想了解完整的组件库搭建,你可以先看使用Vite和TypeScript带你从零打造一个属于自己的Vue3组件库,这篇文章有详细介绍。当然如果你只想知道一个button组件如何开发出来的,只看这篇也就够了。(样式部分参造了elementui组件库)。扫码关注公众号获取Vue3组件库完整搭建源代码

首先我们先看下我们这个button组件要实现的功能

  • 使用type,plain属性来定义按钮基本样式
  • round,size控制按钮形状大小
  • 通过disabled来控制按钮是否可点击
  • 支持icon加入图标增强辨识度

type实现

我们的type可以传入的值可以是primary, success, info,warning, danger分别展示不同按钮颜色,type传入text显示文字按钮(没有边框和背景色的按钮)

这里只展示了一个primary的样式,因为其它值的样式实现是一样的。需要的话可以到button组件样式进行查看。

所以在button/types.ts文件中我们定义一下type的类型:


import { ExtractPropTypes } from 'vue'

export const ButtonType = ['primary', 'success', 'info', 'warning', 'danger','text']

export const buttonProps = {
  type: {
    type: String,
    validator(value: string) {
      //这里表示type只能接收这些值
      return ButtonType.includes(value)
    }
  }
}

export type ButtonProps = ExtractPropTypes<typeof buttonProps>

接下来在button.vue中实现传入不同值赋予不同类名,从而实现显示不同效果。

<template>
    <button class="k-button" :class="styleClass">
        <slot />
    </button>
</template>

<script lang="ts">
import './style/index.less'
import { defineComponent, computed } from 'vue'
import { buttonProps } from './types'
export default defineComponent({
    name: 'k-button',
    props: buttonProps,
    setup(props) {
        const styleClass = computed(() => { 
            return {
                [`k-button--${props.type}`]: props.type
            }
        })

        return {
            styleClass
        };
    },
});
</script>

这样一来传入primary组件就会有个类名k-button--primary吗,传入其它值也一样。然后我们就可以给它们写样式了。进入style/index.less:

.k-button {
  display: inline-block;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  background: #fff;
  border: 1px solid #dcdfe6;
  color: #606266;
  -webkit-appearance: none;
  text-align: center;
  box-sizing: border-box;
  outline: none;
  margin: 0;
  transition: 0.1s;
  font-weight: 500;
  padding: 12px 20px;
  font-size: 14px;
  border-radius: 4px;
  &:hover {
    color: #409eff;
    border-color: #c6e2ff;
    background-color: #ecf5ff;
  }
}
.k-button--primary {
  color: #fff;
  background-color: #409eff;
  border-color: #409eff;
  &:hover {
    background: #66b1ff;
    border-color: #66b1ff;
    color: #fff;
  }
}
.k-button--text {
  border-color: transparent;
  color: #409eff;
  background: transparent;
  padding-left: 0;
  padding-right: 0;
  &:hover {
    color: #66b1ff;
    border-color: transparent;
    background-color: transparent;
  }
}

plain(朴素按钮)和round(圆角按钮)

我们可以通过传入plain和round来决定这个按钮是否为朴素按钮和圆角按钮,很显然它们是个布尔类型

//types.ts

...

export const buttonProps = {
  type: {
    type: String,
    validator(value: string) {
      return ButtonType.includes(value)
    }
  },
  plain: Boolean,
  round: Boolean
}

...

然后在button.vue定义我们的styleClass

//button.vue

const styleClass = computed(() => {
            return {
                [`k-button--${props.type}`]: props.type,
                'is-plain': props.plain,
                'is-round': props.round
            }
        })

最后在style/index.less写上我们的样式


...

.k-button--primary.is-plain {
  color: #409eff;
  background: #ecf5ff;
  border-color: #b3d8ff;
  &:hover {
    color: #fff;
    background-color: #409eff;
    border-color: #409eff;
  }
}
.is-round {
  border-radius: 20px;
}

注意

两个连在一起的类名如:.k-button--primary.is-plain 代表一个元素只有包含这两个类名,定义的样式才生效

禁用按钮

同样的,在types.ts定义disabled的类型

//types.ts

...
export const ButtonType = ['primary', 'success', 'info', 'warning', 'danger']

export const buttonProps = {
  type: {
    type: String,
    validator(value: string) {
      return ButtonType.includes(value)
    }
  },
  plain: Boolean,
  round: Boolean,
  disabled: Boolean
}

...

然后在button.vue定义我们的styleClass

//button.vue

const styleClass = computed(() => {
            return {
                [`k-button--${props.type}`]: props.type,
                'is-plain': props.plain,
                'is-round': props.round,
                'is-disabled': props.disabled
            }
        })

最后添加上我们的样式

...
.k-button.is-disabled {
  color: #c0c4cc;
  cursor: not-allowed;
  background-image: none;
  background-color: #fff;
  border-color: #ebeef5;
}
.k-button--primary.is-disabled,
.k-button--primary.is-disabled:active,
.k-button--primary.is-disabled:focus,
.k-button--primary.is-disabled:hover {
  color: #fff;
  background-color: #a0cfff;
  border-color: #a0cfff;
}
//朴素按钮禁用
.k-button--primary.is-disabled.is-plain,
.k-button--primary.is-disabled.is-plain:active,
.k-button--primary.is-disabled.is-plain:focus,
.k-button--primary.is-disabled.is-plain:hover {
  color: #8cc5ff;
  background-color: #ecf5ff;
  border-color: #d9ecff;
}
...

size

通过size我们可以控制按钮的大小,组件接收的size值有:midium, small, mini。实现方式和上面差不多,这里就直接展示部分代码了

  • types.ts
//types.ts

...

export const ButtonSize = ['midium', 'small', 'mini'];

export const buttonProps = {
  type: {
    type: String,
    validator(value: string) {
      return ButtonSize.includes(value)
    }
  },
  plain: Boolean,
  round: Boolean,
  disabled: Boolean
}

...

  • button.vue
//button.vue

const styleClass = computed(() => {
            return {
                [`k-button--${props.type}`]: props.type,
                'is-plain': props.plain,
                'is-round': props.round,
                'is-disabled': props.disabled,
                [`k-button--${props.size}`]: props.size,
            }
        })
  • style/index.less
//index.less
...
.k-button--medium {
  padding: 10px 20px;
  font-size: 14px;
  border-radius: 4px;
}

.k-button--small {
  padding: 9px 15px;
  font-size: 12px;
  border-radius: 3px;
}

.k-button--mini {
  padding: 7px 15px;
  font-size: 12px;
  border-radius: 3px;
}

最后我们在项目中引用(examples)来查看效果;

<-- App.vue -->
<template>
    <div>
        <Button>默认按钮</Button>
        <Button type="primary">主要按钮</Button>
        <Button type="success">成功按钮</Button>
        <Button type="info">信息按钮</Button>
        <Button type="warning">警告按钮</Button>
        <Button type="danger">危险按钮</Button>
        <Button type="text">文字按钮</Button>
        <br>
        <br>
        <Button plain>朴素按钮</Button>
        <Button type="primary" plain>主要按钮</Button>
        <Button type="success" plain>成功按钮</Button>
        <Button type="info" plain>信息按钮</Button>
        <Button type="warning" plain>警告按钮</Button>
        <Button type="danger" plain>危险按钮</Button>
        <br>
        <br>
        <Button round>圆角按钮</Button>
        <Button type="primary" round>主要按钮</Button>
        <Button type="success" round>成功按钮</Button>
        <Button type="info" round>信息按钮</Button>
        <Button type="warning" round>警告按钮</Button>
        <Button type="danger" round>危险按钮</Button>
        <br>
        <br>
        <Button disabled>禁用按钮</Button>
        <Button type="primary" disabled>主要按钮</Button>
        <Button type="success" disabled>成功按钮</Button>
        <Button type="info" disabled>信息按钮</Button>
        <Button type="warning" disabled>警告按钮</Button>
        <Button type="danger" disabled>危险按钮</Button>
        <br>
        <br>
        <Button disabled>禁用按钮</Button>
        <Button type="primary" disabled plain>主要按钮</Button>
        <Button type="success" disabled plain>成功按钮</Button>
        <Button type="info" disabled plain>信息按钮</Button>
        <Button type="warning" disabled plain>警告按钮</Button>
        <Button type="danger" disabled plain>危险按钮</Button>
        <br>
        <br>
        <Button>默认按钮</Button>
        <Button size="medium">中等按钮</Button>
        <Button size="small">小型按钮</Button>
        <Button size="mini">超小按钮</Button>
    </div>
</template>
<script lang="ts" setup>
import { Button } from 'kitty-ui'
</script>
<style lang="less">
.k-button {
    margin-right: 10px;
}
</style>

启动项目我们就会在浏览器中看到我们的各式各样的button组件了

图标

通过icon属性设置按钮图标,支持Icon组件里的所有图标(Icon组件下一篇文章将会详细介绍)。

  • types.ts中设置icon类型
export const buttonProps = {
  type: {
    type: String,
    validator(value: string) {
      return ButtonType.includes(value)
    }
  },
  plain: Boolean,
  round: Boolean,
  disabled: Boolean,
  icon: String,
  iconPosition: String,
  size: {
    type: String,
    validator(value: string) {
      return ButtonSize.includes(value)
    }
  }
}
  • 修改button组件

icon可以传入icon组件中定义的name,iconPosition可选right使图标在按钮右侧。

<!-- button.vue -->
<template>
    <button class="k-button" :class="styleClass">
        <Icon class="icon" v-if="iconFont.iconName && iconFont.iconPosition != 'right'" :name="iconFont.iconName" />
        <slot />
        <Icon class="icon" v-if="iconFont.iconPosition == 'right' && iconFont.iconName" :name="iconFont.iconName" />
    </button>
</template>

<script lang="ts">
import './style/index.less'
import { defineComponent, computed } from 'vue'
import { buttonProps } from './types'
import Icon from '../Icon/icon.vue'
export default defineComponent({
    name: 'k-button',
    props: buttonProps,
    components: { Icon },
    setup(props) {

        const styleClass = computed(() => {
            return {
                [`k-button--${props.type}`]: props.type,
                'is-plain': props.plain,
                'is-round': props.round,
                'is-disabled': props.disabled,
                [`k-button--${props.size}`]: props.size,
            }
        })

        //图标
        const iconFont = computed(() => {
            const iconName = props.icon
            const position = props.iconPosition
            return {
                iconName,
                iconPosition
            }
        })

        return {
            styleClass,
            Icon,
            iconFont
        };
    },
});
</script>


然后在examples/App.vue使用并查看效果

<template>
    <div>
        <Button type="success" icon="edit">图标按钮</Button>
        <Button type="primary" icon="map" icon-position="right">图标按钮</Button>
        <Button type="primary" icon="ashbin"></Button>
    </div>
</template>
<script lang="ts" setup>
import { Button } from 'kitty-ui'

</script>
<style lang="less">
.k-button {
    margin-right: 10px;
}
</style>

最后

到这里一个button组件的开发基本就结束了,看起来一个不起眼的button组件里面其实还是包含些许内容的。关注我将不定期更新其它组件的实现。

如果你觉得本篇文章对你有帮助的话,动动指头点个赞吧orz,你的鼓励将会是我持续创作的动力。

有关从0搭建Vue3组件库:button组件的更多相关文章

  1. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  2. ruby-on-rails - rails 上的 ruby : radio buttons for collection select - 2

    我有一个集合选择:此方法的单选按钮是什么?谢谢 最佳答案 Rails3中没有这样的助手。在Rails4中,它是collection_radio_buttons. 关于ruby-on-rails-rails上的ruby:radiobuttonsforcollectionselect,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/18525986/

  3. (附源码)vue3.0+.NET6实现聊天室(实时聊天SignalR) - 2

    参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍  介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。  内容有:    ①:Hub模型的方法介绍    ②:服务器端代码介绍    ③:前端vue3安装并调用后端方法    ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke()  去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on

  4. ruby-on-rails - Ruby on Rails 中的 Button_to 错误路由 - 2

    我正在尝试使用button_torails助手。我写了下面的代码:'mark-button'%>得到如下错误信息Noroutematches"/items/1/edit"但是当我刷新页面时,它会执行适当的操作。我得到的页面的URL是localhost:3000/items/1/edit这是正确的网址。如果我切换button_to命令link_to页面加载没有错误。意思是这段代码:'mark-button'%>加载正常。也许有一些特点button_to我不知道,但我迷路了。 最佳答案 我认为您可能误用了button_to。我一直认为,

  5. vue 实现内容超出两行显示展开更多功能,可依据需求自定义任意行数! - 2

    平时开发中我们经常会遇到这样的需求,在一个不限高度的盒子中会有很多内容,如果全部显示用户体验会非常不好,所以可以先折叠起来,当内容达到一定高度时,显示展开更多按钮,点击即可显示全部内容,先来看看效果图: 这样做用户体验瞬间得到提升,接下来看看具体细节。0">主要操作在内容这里{{item.username}},……展开更多样式大家可依据自己项目需求进行设计,这里就不贴了,主要说几个关键的。1、在data中定义三个属性isShowMore:false, //控制展开更多的显示与隐藏textHeight:null, //框中内容的高度status:false, //内容状态是否打开2.计算内容是否

  6. vue3.0 + vite2.0+如何兼容低版本浏览器 - 2

    这里写自定义目录标题一、问题二、解决三、解决方案四、打包预览一、问题在使用vue3.2和vite2开发一个移动端或者钉钉端H5微服务iosapp内置浏览器打开没问题安卓app内置浏览器打开空白页面vconsole打印出现报错globalthisundefind二、解决内置浏览器版本比较低打印出来是63vue3代码不兼容低版本浏览器三、解决方案步骤一:vite.config.ts里build.target配置项指定构建目标为es2015或者步骤二:安装@vitejs/plugin-legacy安装完报错也还在指定版本可以解决“@vitejs/plugin-legacy”:“1.8.0”,步骤三:

  7. Android Studio开发之使用内容组件Content获取通讯信息讲解及实战(附源码 包括添加手机联系人和发短信) - 2

    运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid

  8. LinuxGUI自动化测试框架搭建(二十二)-框架主入口main.py设计&log日志调用 - 2

    (二十二)-框架主入口main.py设计&log日志调用和生成1测试目的2测试需求3需求分析4详细设计4.1新建存放日志目录log4.1.1配置config.py中写入log的目录4.2`baseInfo.py`中加入日志4.3`test_gedit.py`中加入日志4.4主函数入口main.py中调用日志5调用日志主函数main.py源码6`baseInfo.py`源码7`test_gedit.py`源码8运行效果9目前框架结构1测试目的组织运行所有的测试用例,并调用日志模块,便于问题定位。

  9. Vue3的新特性 - 2

    Vue3的新特性包括:CompositionAPI:一种新的API风格,可将有关组件功能的代码逻辑封装在单独的函数中,从而更好地管理和重用代码。Teleport:可以让组件在DOM层次结构中的任何位置渲染。Suspense:一种新的异步渲染模式,可以优化应用程序的性能。更快的渲染速度:Vue3使用了新的虚拟DOM算法,并且对渲染过程进行了优化,因此在渲染大型应用时性能更高。更小的包大小:Vue3的打包大小比Vue2更小,因为它不再需要依赖像vue-template-compiler这样的工具。其他改进:Vue3还具有其他一些改进,例如更好的TypeScript支持、更好的错误提示和更好的调试工

  10. 基于ActiveMQ搭建MQTT服务备忘(二):webapp集成 - 2

    (1)为什么写这个话题(Why)读万卷书不如行千里路。这次搭建MQTT服务,遇到了一些误解,特此记录备忘。主要包括:(1)服务(Broker)的账户管理与网页管理平台的账户(2)与web应用的集成(Spring系)(2)ActiveMQ版本选择因为JAVA环境是JDK8,所以按兼容性考虑选择了ActiveMQ5.15的最后版本5.15.15。如果你是JDK11则可考虑ActiveMQ的最新版本5.17或5.18。ActiveMQ支持MQTTv3.1.1andv3.1。(3)ActiveMQ与web应用的集成主要介绍与Spring系的webapp集成(SpringBoot和SpringMVC)。

随机推荐