第三集-从零开始实现一套pc端vue的ui组件库button组件其一

9次阅读

共计 5656 个字符,预计需要花费 15 分钟才能阅读完成。

第三集: 从零开始实现(button 组件 1)

本集定位:
为什么要叫 1 那, 因为我感觉这个组件细节比较多, 应该会讲很多内容, 所以先把基础功能在这一集实现, 下集去做拓展.
button 组件
这是一个基本上每个工程都会用到的组件, 传统的 button 千篇一律的样式, 仿佛按钮不做的一样就没法用似的, 我偏要加一些别人没加过的样式来玩, 做过项目的小伙伴都会遇到一个问题, 防抖与节流, 而这么主要的功能竟然没有 ui 组件库的按钮来集成, 这次我就把这两个功能集成到按钮式搞一搞, ???? 开始行动吧.
创建组件基本结构
继续秉承 bem 的思想, button 总体结构如下:

index.js 文件 还是老套路, 组件的 name 属性很重要.

import Button from './main/button.vue'

Button.install = function(Vue) {Vue.component(Button.name, Button);
};

export default Button

button.vue

<template>
  <button class="cc-button">
    <slot />
  </button>
</template>

载体选用 button 原生标签, 不选择 div 标签去模拟, 因为 button 本身有很多原生属性, 而且语义化也要好过 div. slot 标签当然是为了接到用户在 button 中间输入的信息.

按钮第一条, 当然是先分个类型开开胃

<template>
  <button class="cc-button"
          :class="[type ? 'cc-button--' + type : '']"
          :type="nativeType"
    <slot />
  </button>
</template>
<script>
export default {
  name: "ccButton",
  props: {
    type: String, // 类型
    nativeType: String, // 原生的类型还是要给的
  }
};
</script>

1: vue 的 class 属性很强大的, 他可以数组的形式, 字符串的形式, 对象的形式, 而我采用的是 数组套一切的形式, 接下来对象类型也会在这个 class 数组里面使用.
2: 原生的 type 也要接受, 因为用户会有这样的需求的, 我把它命名为 nativeType, 跟 element 一样.
3: 定义几种相应的 type 样式, 这个 type 属性没做校验, 因为就算用户不按照我给出的范围传值, 无非就是样式没变化, 没有其他影响的, 接下来就是定义按钮的 type 样式了.

Button.scss

@import './common/var.scss';   // 引入定义的所有变量
@import './common/mixin.scss'; // 引入所有的函数
@import './common/extend.scss'; // 引入公共样式
@include b(button) {
    cursor: pointer;           // 鼠标变小手
    align-items: center;       // 轴对齐
    display: inline-flex;    // 开启 flex 模式, 由于还要保持行的特性, 所以是 inline-flex
    vertical-align: middle;    // 中对齐, 为了以后的 icon 准备的
    justify-content: center;   // 居中
    background-color: white;   // 白色
    outline: 0;                 // 去掉聚焦时的轮廓
    border-radius: 6px;         // 感觉圆角 6 还是挺友好的, 没有做成可配置
    transition: all 0.1s;       // 动画当然要有, 交互体验很重要
    &:active {             // 点击的时候
        box-shadow: none;   // 嘿嘿这个是我的 ui 组件独有的风格
        opacity: 0.7;        // 微微一暗, 以示点击到了
        transform: translateX(2px) translateY(2px) scale(0.9); // 会有个小小的位移
    }
    &:hover { // 悬停变色
        background-color:rgba(0,0,0,0.06)
    }
    @include commonShadow($--color-black); // 这个下面会说????
    @at-root {@include commonType(cc-button--)  // 这个下面会说????
    };
}

var.scss 文件, 本项目采用的基本配色, 毕竟不是专业的大家见谅????

// 基本色
$--color-black:#000000 !default;
$--color-white:#FFFFFF !default;
// 基本色鲜艳
$--color-nomal:#409EFF !default;
$--color-success:#7CCD7C !default;
$--color-warning:#FFB90F !default;
$--color-danger: #FF0000 !default;
$--color-disabled: #bbbbbb !default;
$--color-difference: #F5F7FA !default;
// 字体
$--size-big: 16px;
$--size-nomal: 15px;
$--size-small: 14px;
// 输入框
$--color-input-placeholder:#aaa

!default; 这个东西的意思是优先级最低, 可以被其他的顶掉.

mixin.scss

@mixin commonShadow($color) {
  @if $color== 'success' {$color: $--color-success;}

  @if $color== 'warning' {$color: $--color-warning;}

  @if $color== 'danger' {$color: $--color-danger;}

  @if $color== 'disabled' {
    cursor: not-allowed;
    $color: $--color-disabled;
  }

  color: $color;
  border: 1px solid $color;
  box-shadow: 2px 2px $color;
}


@mixin commonType($name) {@each $type in (success, warning, danger) {.#{$name}#{$type} {@include commonShadow($type);
    }
  }
}

1: 先说 commonShadow 这个函数吧, 这个是本套 ui 框架的特色样式 (惨不忍睹) , 就是想做点样子不一样的, 这个函数很简单, 会根据传进来的不同 $color 值来进行样式的返回, 只要使用这个函数就会为元素加上阴影效果, 注意 @if 后面不要习惯性的协商小括号
2: 由于样式不止一个, commonType 函数应运而生, 他把我定义的几种样式类型以数组的形式进行循环,@each 后面接的是每次循环出来的 item, in 后面的() 里面的是要被循环的量
3: .#{$name}#{$type} 就是传进来的量与 item 的拼接, #{}就可以在 sass 里面进行拼接, 并不是 id 的意思, 我传入的是 ‘cc-button–‘ 所以这个函数定义了 ‘cc-button–success’, ‘cc-button–warning’,’cc-button–danger’ 三种样式加上了对应颜色的阴影效果, 如图

点击等更多效果可以来我的博客查看 哈哈????;

@at-root
这个关键词没有使用过的小伙伴注意了
1: 这个关键词作用是, 使样式跳出{} 大括号范围, 比如我在 cc-button 这个 class 名下写的样式, 然后我用如下写法:

.cc-button{.cc-button--success{}
} 

以上写法的意思是, .cc-button 里面有个 class 为.cc-button–success 的元素, 为这个元素赋予相应属性, 这显然不是我想要的,

.cc-button{
  @at-root {.cc-button--success{}
  }
} 

相当于

.cc-button{ } 
.cc-button--success{}

所以我说他相当与跳出 {} 大括号.

点击与 disabled 禁用属性
我把这个属性排在第二位来写, 因为有点击就有不可点击与其相对, 这属于的最基本的功能.

<button class="cc-button"
          @touchstart='touchstart($event)'
          :class="[
          type ? 'cc-button--' + type : '',
    {'is-disabled':disabled,}]":type="nativeType"@click="$emit("click")">
    <slot />
  </button>
  1. touchstart 莫名其妙出现个是不是很诧异, 因为我发现苹果手机不加这个属性的话, 按钮是无法点击的, 保险起见还是加上吧, 万一运营人员在家改点东西, 结果发现手机上点不了, 就坏菜了.
  2. 在 class 属性的数组里面加入一个对象也是可以使用的, vue 的强大解析能力, 如果用户传了 disabled, 就使用 is-disabled 这个 class 名
  3. @click 事件要给用户返回, 因为这个是组件, 如果用户想要触发点击事件, 需要自己写.native 修饰符, 避免用户的多余代码, 这里我来主动处理, 这个 click 事件接下来还要进行重构, 因为要配合节流与防抖
  4. 小技巧: disabled 属性我, 默认是布尔值, 这样用户如果只是传了个 disabled 就是 true 了, 不用写:disabled=’true’, 如果我不写默认是布尔类型的话, 是 undefined, 为了用户方便书写的小技巧.
disabled: Boolean

禁用样式的属性 Button.scss 改装

    &:not(.is-disabled) {
        &:active {
            box-shadow: none;
            opacity: 0.7;
            transform: translateX(2px) translateY(2px) scale(0.9);
        }
        &:hover {background-color:rgba(0,0,0,0.06)
        }
    }
    @include when(disabled) {@include commonShadow(disabled);
    }

1: 只有在不是禁用状态的时候, 才有点击效果, 悬停效果
2: 当 disabled 时, 才显示 disabled 的专属皮肤

when 函数

@mixin when($state) {
  @at-root {&.#{$state-prefix + $state} {@content;}
  }
}

1: 见名知意, 当什么什么的时候, 用到了 @at-root 属性, 忘了的去上面看下, @content; 表示的是类里面的样式内容, 很神奇吧!

.box{color:red;}

1: 上面的 color:red; 就是 @content;
2: 所以说这个函数就是一个命名函数, 可以抽象出前缀, 而且是在 {} 外的平级样式.
3: 禁用状态有 cursor: not-allowed; 属性, 让鼠标呈现禁用状态

左中右按钮 (文章字数写多了好卡)

  1. 按钮经常被拿来组合着用, 比如整齐的一排 , 那么我就第一想法就是给他个圆角
  2. 从用户体验的角度来说, 这个跟 disabled 属性应该是平级的, 把关键词写在标签里面就能生效, 也是布尔类型
  3. 命名与 element 一样采用 is- 的形式, 分为 is-left is-right is-centre
<template>
  <button class="cc-button"
          @touchstart='touchstart($event)'
          :class="[
          type ? 'cc-button--' + type : '',
    {
      'is-left':left,
      'is-right':right,
      'is-centre':centre,
      'is-disabled':disabled,
    }]":type="nativeType"@click="click">
    <slot />
  </button>
</template>
  1. 为什么要分着写 , 因为如果靠一个变量来控制的话又会出现用户 要写 :xxx=’xxx’ 这种写法, 我是用户我无法接受

样式:

 @include when(left) {border-radius: 16px 0 0 16px;}
    ;
    @include when(right) {border-radius: 0 16px 16px 0;}
    ;
    @include when(centre) {border-radius: 0;}

无非是调了一下圆角, 但是特别好玩

效果如下⬇️


按钮大小

这个很多 ui 库都有时间, 那我也实现一下吧, 感觉一般般.

:class="[ 
          sizeType, // 新加一个属性
          type ? 'cc-button--' + type : '',
    {
      'is-left':left,
      'is-right':right,
      'is-centre':centre,
      'is-disabled':disabled,
    }]"
    
    props: {
         size: {
          typr: String,
          default: "normal"
        }
    }
   computed: {sizeType() {let sizeList = ["big", "small"];
      if (sizeList.includes(this.size)) return "size-" + this.size;
      return "size-normal";
    }
  }
  1. size 分为 big small normal , 对用户的输入进行验证, 不通过就返回正常大小
  2. 这个没啥技术含量
&.size-big {
        font-size: $--size-big;
        padding: 6px 11px;
    }
    &.size-normal {
        font-size: $--size-nomal;
        padding: 4px 8px;
    }
    &.size-small {
        font-size: $--size-small;
        padding: 2px 6px;
    }

未完待续

  1. 下一篇继续玩按钮组件, 主要有为按钮添加 icon, 防抖与节流
  2. 文章字数多了, 电脑好卡

github: 项目地址
个人博客: 个人博客

正文完
 0