写点什么

vue+typescript 实现组件封装之 button 篇

用户头像
小小
关注
发布于: 2021 年 03 月 25 日

前言

最近工作不是很忙,于是计划封装一个内部的组件库,平时习惯了使用别人封装好的组件, 并没有深究其中原理,只觉得并不是很难,但是当自己去揭开这层面纱后, 发现里面有非常多的点值得学习, 所以记录下来分享给大家

明确目标

在封装 Button 组件之前,我们需要先分析,希望 Button 有哪些特性及功能。

按钮功能概念图

本次项目我们采用 Vue3.0 + TypeScript 来实现 UIButton 的封装,我们这篇文章会从零开始来介绍按钮组件的封装。

创建工程

我们使用 Vue CLI 来创建一个vue3 + TS的工程,首先检查一下 Vue CLI 的版本:

vue -V@vue/cli 4.3.1

如果大家的版本低于 4.3.1,想要升级的话, 可以在命令行中输入npm i -g @vue/cli进行手动升级。

首先我们创建一个 vue3 的项目:


具体的创建过程和创建 vue2 项目没有什么区别,这里就不一步步介绍了, 说一下我这个项目选择了哪些选项:


实现基础按钮

首先我们需要实现一个基础的按钮, 包括按钮默认的一些样式、按钮标题以及按钮点击事件处理。在components文件夹下创建button文件夹,文件夹内创建UIButton.vue文件:

// UIButton.vue<template>    <button        @click="onClickBtn"        class="ui-btn"    >        <slot>按钮</slot>    </button></template>
<script lang="ts">import { Component, Vue, Emit } from "vue-property-decorator";
@Componentexport default class App extends Vue { @Emit("click") private emitClick(event: MouseEvent) {} // 创建私有方法 private onClickBtn(event: MouseEvent) { if (!this.disabled) { this.emitClick(event); } }}</script>
<style lang="stylus" scoped>.ui-btn min-width 64px height 36px padding 0 16px border 0 solid black border-radius 4px font-size 0.875rem font-weight 500 letter-spacing 0.09em cursor pointer color #17233d background-color #2d8cf0 outline none user-select none</style>
复制代码


从上面代码可以看出,UIButton其实是对原生的 button 的一个封装,上面这个按钮组件实现了:

  1. 按钮的基本样式

  2. 按钮标题可以在组件使用时写

  3. 按钮点击事件处理

接下来可以对封装的 UIButton组件进行使用:

 <UIButton @click="onClick" ></UIButton> <UIButton @click="onClick" >我的Button</UIButton>
复制代码


添加尺寸控制

封装几个常用的尺寸,例如迷你型(mini)、小型(small)、普通按钮(normal)、大型按钮(large)以及长按钮(long),使用时根据需求设置尺寸,不同尺寸的大小,他们的高度、字体大小以及 padding 值是不同, 首先展示一下不同尺寸封装完展示效果:



首先我们需要处理不同尺寸的样式,给不同尺寸设置不同的 class 来控制样式, 所以UIButton组件的 class 需要修改成动态的:

 <button        @click="onClickBtn"        :class="[            'ui-btn',            size ? `ui-btn--${size}` : ''        ]"    >        <slot>按钮</slot>    </button>
复制代码


size 是通过在组件是传入的, 所以使用@Prop接受:

import { Component, Vue, Emit, Prop } from "vue-property-decorator";export default class App extends Vue {    // 尺寸 mini small normal large    @Prop(String) private size: string | undefined;}
复制代码


最后设置ui-btn--mini,ui-btn--small等样式, 由于我们使用 stylus 的混合来实现(类似于 js 的函数),不了解 stylus 混合的小伙伴自行学习一下。

@import "../style/var.styl";resize(minWidth, height, paddingLR, fontSize)    min-width minWidth    height height    padding 0 paddingLR    font-size fontSize.ui-btn    ...    &.ui-btn--mini        resize($button-mini-width, $button-mini-height, $button-mini-paddingLR,$button-mini-font-size)    &.ui-btn--small        resize($button-small-width, $button-small-height, $button-small-paddingLR,$button-small-font-size)    &.ui-btn--large        resize($button-large-width, $button-large-height, $button-large-paddingLR,$button-large-font-size)    &.ui-btn--normal        resize($button-normal-width, $button-normal-height, $button-normal-paddingLR,$button-normal-font-size)    &.ui-btn--long        width 100%
复制代码


其中$button-mini-width$button-mini-height这些变量是我在var.styl文件中定义好的全局变量, 文末 github 地址可以查看完整代码。这样我们就要定义好了不同 size 的 button 按钮, 使用如下:

<UIButton size="mini">超小</UIButton><UIButton size="small">小</UIButton><UIButton size="large">超大</UIButton>
复制代码


控制按钮形状

提供的形状包含矩形按钮、圆角矩形、圆角按钮以及圆形按钮,默认情况下的按钮是圆角矩形的,下图为我们要封装的组件包含的形状


通过传递squareroundcircle分别代表矩形、圆角和圆形按钮,首先动态绑定 class 来控制按钮圆角样式

 <button        @click="onClickBtn"        :class="[            'ui-btn',            size ? `ui-btn--${size}` : '',            {                'ui-btn--square': square,                'ui-btn--round': round,                'ui-btn--circle': circle,            },        ]"    >        <slot>按钮</slot>    </button>
复制代码


squareroundcircle是通过组件使用是传递的, 在UIButton中需要 prop 接受这个三个变量:

//形状@Prop(Boolean) private round: boolean | undefined; //圆角@Prop(Boolean) private square: boolean | undefined; //方形@Prop(Boolean) private circle: boolean | undefined; //圆形
复制代码


在写 stylus 要注意一点,圆形和圆角按钮的border-radius值与按钮高度有关, 所以可以写在上边定义的resize混合中:

resize(minWidth, height, paddingLR, fontSize)    min-width minWidth    height height    padding 0 paddingLR    font-size fontSize    &.ui-btn--round, &.ui-btn--circle        border-radius (@height / 2)    &.ui-btn--circle        width @height        min-width 0        padding 0.ui-btn    ...    &.ui-btn--square        border-radius 0
复制代码


使用如下:

<UIButton square="square">矩形按钮</UIButton><UIButton >正常按钮</UIButton><UIButton round="round">圆角按钮</UIButton><UIButton circle="circle">圆形</UIButton>
复制代码


设置按钮颜色

颜色包括按钮的背景颜色、文本颜色的控制。


从图中可以看到,通过传入color就可以设置主色;传入titleColor控制按钮标题的颜色, 当不设置标题颜色时, 采用默认的标题颜色。

<!-- template --> <button        @click="onClickBtn"        :class="[            'ui-btn',            size ? `ui-btn--${size}` : '',            {                'ui-btn--round': round,                'ui-btn--square': square,                'ui-btn--circle': circle,            },        ]"        :style="            `            --color-tint: ${TintColor};            --color-title: ${TitleColor}        `        "    >        <slot>按钮</slot>    </button>
复制代码


上面代码中使用了两个 stylus 变量--color-tint--color-title,分别用来表示按钮主色和标题颜色,这里我们需要使用到计算属性来获取颜色值,在类组件中定义计算属性和定义方法很相似, 只是方法前边需要加一个get,上面有获取颜色值的是两个计算属性TintColor以及TitleColor。先看一下两个计算属性如何定义的:

// script@Prop(String) private color: string | undefined; //主色@Prop(String) private titleColor: string | undefined; //标题颜色// 计算属性private get TintColor() {    if (this.color) {        return this.color;    }    return "#2d8cf0";}
private get TitleColor() { if (this.titleColor) { return this.titleColor; } return "#17233d";}
复制代码


看上面的代码,有的小伙伴可能会疑惑,为什么不直接使用colortitleColor, 而是要用计算属性呢?是因为当我们没有传入color或者titleColor时, 我们获取到的 undefined, 将 undefined 赋值给 stylus 变量,会导致没有默认颜色, 所以这里才用了计算属性。 当然也可以通过给接收到的color写默认值, 这种方式也是可行的。

接下来看一下 stylus 样式中是如何来定义--color-tint--color-title两个变量的, 在样式中添加如下两句代码即可

.ui-btn    min-width 64px    ...    color var(--color-title, #17233d)    background-color var(--color-tint, #2d8cf0)    ...
复制代码


好啦!按钮颜色相关的设置完成了,接下来我们看一下如何给按钮设置禁用

设置禁用

实际开发中禁用按钮是非常常见的,一般都是设置一个disable属性, 那我们也这样来做。

button 标签中根据disable值动态绑定类名ui-btn--disabled, 这里我暂时隐藏一些已经实现但不相关的代码:

<!-- template --> <button        @click="onClickBtn"        :class="[            'ui-btn',            size ? `ui-btn--${size}` : '',            {                ...                'ui-btn--disabled': disabled            },        ]"        "    >        <slot>按钮</slot>    </button>
复制代码


UIButton中接收 disable

// 禁用@Prop(Boolean) private disabled: boolean | undefined;
复制代码


然后在添加ui-btn--disabled样式:

ui-btn    &.ui-btn--disabled        background-color #efeff6        color #bbbdc1        cursor not-allowed
复制代码


设置阴影

给按钮添加阴影,使用 css3 属性box-shadow给元素设置阴影:


这里就不过多的介绍box-shadow每个值代表的意义, 不清楚的可以去学习一下。

我们希望可以直接给UIButton组件添加一个 shadow 属性, 并且可以给shadow 赋值, 比如:

<UIButton shadow="2">阴影2</UIButton><UIButton shadow="5">阴影5</UIButton>

然后对应在组件中就可以通过ui-shadow-2ui-shadow-5 这样的类来控制:

<template>    <button        @click="onClickBtn"        :class="[            'ui-btn',            ShadowValue,            ...        ]"    >        <slot>按钮</slot>    </button></template>
<script lang="ts">@Prop([String, Number]) readonly shadow: string | number | undefined;private get ShadowValue() { if(this.shadow){ return `ui-shadow--${this.shadow}` } return `ui-shadow--0`}</script>
<style lang="stylus">Shadow(a, b, c) box-shadow 0 a rgba(0,0,0,0.2), 0 b rgba(0,0,0,0.14), 0 c rgba(0,0,0,0.12).ui-btn &.ui-shadow--1 Shadow(2px 1px -1px, 1px 1px 0, 1px 3px 0) &.ui-shadow--2 Shadow(3px 1px -2px, 2px 2px 0, 1px 5px 0) &.ui-shadow--3 Shadow(3px 3px -2px, 3px 4px 0, 1px 8px 0) &.ui-shadow--4 Shadow(2px 4px -1px, 4px 5px 0, 1px 10px 0)</style>
复制代码


按钮类型设置

我们封装的按钮想要提供线框按钮、文本按钮以及图标按钮可供选择, 功能效果如下:


通过设置属性

  • loading控制加载按钮;

  • icon设置图标按钮;

  • text设置文本按钮;

  • border设置边框按钮

通过设置类名ui-btn--loadingui-btn--text以及ui-btn--icon;其中边框设置了ui-btn--dashedui-btn--solid来控制实现边框与虚线边框:


<!-- template --> <button        :class="[            ...            border === 'dashed'                ? 'ui-btn--dashed'                : border                ? 'ui-btn--solid'                : '',            {                ...                'ui-btn--loading': loading,                'ui-btn--text': text,                'ui-btn--icon': icon,            },        ]"    >        <div v-if="loading" class="ui-loading-circle ui-icon--default ">            <img src="../../../assets/icons/loading.gif" alt="" />        </div>        <div v-if="icon" class="ui-icon--default">            <img :src="icon" alt="" />        </div>        <span v-if="$slots.default"><slot>按钮</slot></span>        <slot v-else>按钮</slot>    </button>
复制代码


组件接受参数定义:

// 按钮类型    @Prop(Boolean) readonly text: boolean | undefined; //文本按钮    @Prop([Boolean, String]) readonly border: boolean | string | undefined; // 边框按钮    @Prop(Boolean) readonly loading: string | undefined; // 加载中按钮    @Prop(String) readonly icon: string | undefined; // 图标按钮
复制代码


最后设置的类的样式:

&.ui-btn--loading, &.ui-btn--icon    position relative    vertical-align middle    .ui-icon--default        display inline-block        margin-right 4px        line-height 0        >img            vertical-align: middle            width 1em            height 1em            display inline-block&.ui-btn--solid    border 1px solid var(--color-title, #17233d)&.ui-btn--dashed    border 1px dashed var(--color-title, #17233d)&.ui-btn--text,&.ui-btn--solid    background-color transparent
复制代码


发布于: 2021 年 03 月 25 日阅读数: 15
用户头像

小小

关注

还未添加个人签名 2019.08.05 加入

还未添加个人简介

评论

发布
暂无评论
vue+typescript实现组件封装之button篇