前言
最近工作不是很忙,于是计划封装一个内部的组件库,平时习惯了使用别人封装好的组件, 并没有深究其中原理,只觉得并不是很难,但是当自己去揭开这层面纱后, 发现里面有非常多的点值得学习, 所以记录下来分享给大家
明确目标
在封装 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 的一个封装,上面这个按钮组件实现了:
按钮的基本样式
按钮标题可以在组件使用时写
按钮点击事件处理
接下来可以对封装的 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>
复制代码
控制按钮形状
提供的形状包含矩形按钮、圆角矩形、圆角按钮以及圆形按钮,默认情况下的按钮是圆角矩形的,下图为我们要封装的组件包含的形状
通过传递square、round、circle分别代表矩形、圆角和圆形按钮,首先动态绑定 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>
复制代码
square、round、circle是通过组件使用是传递的, 在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";}
复制代码
看上面的代码,有的小伙伴可能会疑惑,为什么不直接使用color和titleColor, 而是要用计算属性呢?是因为当我们没有传入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-2、ui-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--loading、ui-btn--text以及ui-btn--icon;其中边框设置了ui-btn--dashed和ui-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
复制代码
评论