【Nova UI】十二、打造组件库之按钮组件(上):迈向功能构建的关键一步

序言

在上一篇文章中,我们深入探索了 icon 组件从测试到全局注册的全过程🎯,成功为其在项目中稳定运行筑牢了根基。此刻,组件库的建设之旅仍在继续,我们将目光聚焦于另一个关键组件 ------ 按钮组件。按钮作为用户与界面交互的核心纽带🧐,其功能的完备性、样式的美观性以及操作的流畅性,都对用户体验起着至关重要的作用。接下来,让我们一同深入剖析按钮组件的实现过程,为组件库增添强大助力,使其在前端开发的舞台上绽放更加耀眼的光芒✨。

UI

我们先借鉴 Element Plus 组件库的样式,着手实现一个基础的按钮组件。参考图如下:

从图中可以分析出,该按钮具备defaultprimarysuccessinfowarningdanger这几种场景,它们的区别主要体现在边框颜色、字体颜色以及背景颜色上。结合我们的实际业务需求,我们开始打造自己的按钮组件

准备工作

按照惯例,我们首先在packages/components/button/src目录下,新增button.tsbutton.vue文件📄。

props属性

button.ts文件中,定义我们所需的props。这些属性包括场景、大小、是否禁用、是否处于加载中、加载时的图标、按钮的前缀图标、后缀图标、按钮形状以及按钮类型。

ts 复制代码
import { ExtractPropTypes, PropType } from 'vue'
import { useSceneProp, useSizeProp, useLoadingIconProp, useIconProp } from '@nova-ui/hooks'

export const buttonShapes = ['round', 'circle'] as const
export type ButtonShapeType = typeof buttonShapes[number]

export const buttonTypes = ['plain', 'text', 'link', 'dashed'] as const
export type ButtonTypeType = typeof buttonTypes[number]

export const buttonProps = {
  scene: useSceneProp(),
  size: useSizeProp(),
  disabled: {
    type: Boolean,
  },
  loading: {
    type: Boolean,
  },
  loadingIcon: useLoadingIconProp(),
  prefixIcon: useIconProp(),
  suffixIcon: useIconProp(),
  shape: {
    type: String as PropType<ButtonShapeType>
  },
  type: {
    type: String as PropType<ButtonTypeType>
  }
} as const

export type ButtonType = ExtractPropTypes<typeof buttonProps>

相关代码如上述所示。其中,scene场景属性很可能在多个组件中复用,因此我们将其单独提取出来(这是一个良好的编程习惯,当相同代码在多处使用时,建议提取以提高代码的可维护性)。在packages/constants目录下新增scene.ts文件,用于存储场景的常量及其类型。同时,在packages/hooks/use-scene目录下新增index.ts文件,编写我们需要使用的props属性值。

ts 复制代码
// packages/constants/scene.ts
export const scenes = ['primary', 'success', 'warning', 'danger', 'error', 'info'] as const
export type Scene = typeof scenes[number]
// packages/hooks/use-scene/index.ts
import { PropType } from 'vue'
import { Scene } from '@nova-ui/constants'
export const useSceneProp = () => ({
  type: String as PropType<Scene>,
})

其它如sizeloadingIconprefixIconsuffixIcon等属性的提取方式与之类似,在此不再一一赘述。

模板部分:构建按钮的外观与交互结构

首先来看看这段代码的 HTML 模板部分,它决定了按钮在页面上的最终呈现效果,是按钮组件的 "外观设计师"👨‍🎨。

html 复制代码
<template>
  <button
    :class="[
      ns.b(),
      ns.m(scene),
      ns.m(size),
      ns.is('disabled', disabled),
      ns.is('loading', loading),
      ns.is(!shape, !!shape),
      ns.is(!type, !!type),
    ]"
  >
    <template v-if="loading">
      <slot v-if="$slots.loading" name="loading"></slot>
      <NIcon v-else :class="ns.e('loading')" :name="loadingIcon"></NIcon>
    </template>
    <template v-if="$slots.prefix || prefixIcon">
      <slot v-if="$slots.prefix" name="prefix"></slot>
      <NIcon v-else-if="prefixIcon" :class="ns.e('prefix')" :name="prefixIcon"></NIcon>
    </template>
    <slot></slot>
    <template v-if="$slots.suffix || suffixIcon">
      <slot v-if="$slots.suffix" name="suffix"></slot>
      <NIcon v-else-if="suffixIcon" :class="ns.e('suffix')" :name="suffixIcon"></NIcon>
    </template>
  </button>
</template>

这段模板代码定义了按钮组件的可视化结构与交互元素。最外层的<button>标签构建了按钮的基本框架,通过:class绑定一系列动态类名,实现按钮外观的多样化。

ns.b()提供了按钮的基础类名,是按钮样式的基石。ns.m(scene)ns.m(size)则根据传入的scene(场景)和size(尺寸)参数,为按钮添加相应的修饰类名,使按钮能够适配不同的使用场景与布局需求。

ns.is系列函数依据传入的布尔值属性,动态添加对应的状态类名。例如,ns.is('disabled', disabled)disabledtrue时,为按钮添加表示禁用状态的类名,改变按钮的外观以提示用户该按钮当前不可操作。同理,ns.is('loading', loading)根据loading状态添加或移除加载相关的类名,实现加载状态下按钮的视觉反馈。

在按钮内容方面,代码充分利用 Vue 的插槽机制与NIcon组件,实现了高度的灵活性。当按钮处于loading状态时,首先检查是否存在名为loading的插槽。若有,则渲染该插槽内容,允许开发者自定义加载状态下的显示元素;若没有,则渲染NIcon组件作为加载图标,图标类名由ns.e('loading')生成,图标名称则由loadingIcon属性指定。

对于按钮的前缀和后缀部分,同样采用了灵活的判断逻辑。先判断是否存在prefix插槽,若有则渲染插槽内容;若没有但设置了prefixIcon属性,则渲染NIcon组件作为前缀图标,类名由ns.e('prefix')生成,图标名称由prefixIcon指定。后缀部分的逻辑与之相同,通过这种方式,按钮可以轻松添加各种图标或自定义内容,极大地增强了按钮的功能性与美观性。

脚本部分:赋予组件功能与数据交互能力

脚本部分是按钮组件的核心,负责引入必要的模块、定义组件的配置与属性,如同为组件注入灵魂🧠。

ts 复制代码
<script lang="ts" setup>
  import { useNamespace } from '@nova-ui/hooks'
  import { buttonProps, ButtonType } from './button'
  import { NIcon } from '@nova-ui/components'

  const ns = useNamespace('button')
  defineOptions({
    name: 'NButton',
  })
  defineProps(buttonProps)
</script>

useNamespace函数用于生成具有统一规范的类名,确保按钮组件的样式管理清晰且易于维护。buttonPropsButtonType./button文件引入,其中buttonProps定义了按钮组件可接收的外部属性,如disabledloadingsize等,为组件与外部的数据交互提供了接口。

NIcon组件从@nova-ui/components引入,用于在按钮中显示各种图标。通过const ns = useNamespace('button')创建了按钮组件专属的样式命名空间实例,供模板部分生成类名使用。

defineOptions({name: 'NButton'})为按钮组件定义了名称NButton,方便在 Vue 项目中进行识别与引用。defineProps(buttonProps)则依据buttonProps定义了组件可接收的属性,将外部传入的数据与组件内部逻辑连接起来,使组件能够根据不同的属性值呈现出相应的状态与外观。

综上所述,这段代码通过模板与脚本的协同工作,实现了一个功能丰富、可定制性强的 Vue 按钮组件。它不仅能够满足各种常见的按钮使用场景,还为开发者提供了灵活的扩展空间,在前端组件库中具有重要的应用价值 。

🦀🦀感谢看官看到这里,如果觉得文章不错的话🙌,点个关注不迷路⭐。

诚邀您加入我的微信技术交流群🎉,群里都是志同道合的开发者👨‍💻,大家能一起交流分享摸鱼🐟。期待与您在群里相见🚀,咱们携手在开发路上共同进步✨ ! 👉点我
感谢各位大侠一路相伴,实在感激! 不瞒您说,在下还有几个开源项目 📦,它们就像精心培育的幼苗 🌱,急需您的浇灌。要是您瞧着还不错,麻烦动动手指,给它们点亮几颗 Star ⭐,您的支持就是它们成长的最大动力,在此谢过各位大侠啦!

相关推荐
qq_252496399611 分钟前
react 子组件暴露,父组件接收
前端·javascript·react.js
fakaifa15 分钟前
【最新版】西陆健身系统源码全开源+uniapp前端
前端·小程序·uni-app·开源·php·约课小程序·健身小程序
南囝coding20 分钟前
关于我的第一个产品!
前端·后端·产品
iOS阿玮27 分钟前
别等了,今天是Xcode15时代的最后一天。
前端·app·apple
沙尘暴炒饭33 分钟前
vuex持久化vuex-persistedstate,存储的数据刷新页面后导致数据丢失
开发语言·前端·javascript
2401_8370885036 分钟前
CSS清楚默认样式
前端·javascript·css
zwjapple1 小时前
React 的 useEffect 清理函数详解
前端·react.js·前端框架
Jewel1051 小时前
如何配置Telegram Mini-App?
前端·vue.js·app
s11show_1632 小时前
hz修改后台新增keyword功能
android·java·前端
二个半engineer2 小时前
Web常见攻击方式及防御措施
前端