手摸手带你封装Vue组件库(5)全局样式以及Button组件

在开发组件之前,我们需要规划好我们的全局样式变量,比如主题色以及不同状态的颜色(警告色、错误颜色、信息颜色等),以及是否你需要规划暗夜模式,这些东西都建议提前考虑。

主题色设置

我建议将 packages/theme-chalk/index.less 文件作为样式的入口文件,然后将基础的样式以及我们后面组件的样式导入其中。 我们可以创建如下结构来管理我们的样式(这个根据自己的喜好来规范),我们在 index.less 中导入我们的各个文件

less 复制代码
@import url(./common/base.less);
@import url(./common/theme.less);

base.less中我存放的是需要全局注册的各种主题变量,我设置了主要这几种状态 default、primary、success、info、warning、danger

less 复制代码
:root {
  --t-default: #172b4d;
  --t-primary: #5e72e4;
  --t-success: #2dce89;
  --t-info: #11cdef;
  --t-warning: #fb6340;
  --t-danger: #f5365c;

  --t-border-color: #dcdfe6;
  --t-bg-color: #fff;
  --t-text-color: #606266;
  --t-placeholder-color: #bfc3d6;
  --t-hover-color: #f2f2f2;
  --t-icon-fill-color: #ccc;
  --t-shadow: rgba(0, 0, 0, 0.12);

  --t-primary-lighten: lighten(#5e72e4, 23%);
  --t-success-lighten: lighten(#2dce89, 45%);
  --t-info-lighten: lighten(#11cdef, 39%);
  --t-warning-lighten: lighten(#fb6340, 35%);
  --t-danger-lighten: lighten(#f5365c, 35%);

  --t-success-border: lighten(#2dce89, 30%);
  --t-info-border: lighten(#11cdef, 30%);
  --t-warning-border: lighten(#fb6340, 30%);
  --t-danger-border: lighten(#f5365c, 30%);
}

theme.less 中存放明暗模式基础的一些颜色变量

less 复制代码
:root,
[data-color-mode="light"] {
  --background-color: #fff;
  --title: #292d35;
  --icon: #9da2ac;
  --border: #e5e5e5;
  --borderColor: #dee2e6;
  --fontColor: #333;
  --iconColor: #bdc0d3;
  --disabled: #b1b1b1;
}

[data-color-mode="dark"] {
  --background-color: #1a1d24;
  --fontColor: #f2f2f2;
  ...;
}

ps. 提前定义好文字、背景、图标等很常见且用的最多的变量,一定要记得在开发组件的时候使用他们,免得回头定义暗夜模式的时候折腾

fonts 文件夹我们准备存放图标的 font 文件,你也可以存放你的字体等

以上的仅供参考,你们只要理解思路就行。

Button 组件开发场景以及需求分析

又是一个看腻的组件,但是很多组件需要用到 button,所以需要提前开发,请保持耐心

先从 button 本身的特性开始,button 是用来和用户点击交互的,且不同的交互会有不同的样式,有 hover(悬停)、acitve(按下去)、focus(选中)我们需要给不同的情况设置不同的颜色。

此外,button 需要支持一些我们常见的功能:

  1. 默认按钮以及不同状态
  2. 不同尺寸
  3. 是否需要圆角
  4. 是否携带图标
  5. loading
  6. disabled

当然,你可以根据自己需求来实现你的组件所支持的功能。

实现

默认按钮以及不同状态、不同尺寸、是否需要圆角、禁用

默认情况下的按钮很简单,我们需要吧 button 标签进行封装和包装,至于不同状态我们可以设置 type 属性来控制其颜色以及对应的交互颜色

我们在 components/button/src/button.vue 的同级创建一个 button.js 用户存放 props、emit、枚举等

button.js

js 复制代码
const BUTTON_TYPE = ["default", "primary", "success", "warning", "info", "danger"];
const BUTTON_SIZE = ["", "small", "mini"];

export const ButtonProps = {
  // 类型
  type: {
    type: String,
    default: "default",
    validator(value) {
      return BUTTON_TYPE.includes(value);
    },
  },
  // 尺寸
  size: {
    type: String,
    default: "",
    validator(value) {
      return BUTTON_SIZE.includes(value);
    },
  },
  // 圆角
  round: {
    type: Boolean,
    default: false,
  },
  // 是否禁用
  disabled: {
    type: Boolean,
    default: false,
  },
};
html 复制代码
<template>
  <button
    class="t-button"
    :class="[`t-button__${type}`,`${size && 't-button--' + size}`,{ 'is-round': round },]"
    :disabled="disabled"
  >
    <div class="t-button__inner">
      <span v-if="$slots.default">
        <slot />
      </span>
    </div>
  </button>
</template>

<script setup>
  import { ButtonProps } from "./button";

  defineOptions({
    name: "t-button",
  });

  const BUTTON_TYPE = ["primary", "success", "warning", "info", "danger"];
  const BUTTON_SIZE = ["", "small", "mini"];

  defineProps(ButtonProps);
</script>
less 复制代码
.t-button {
  display: inline-block;
  white-space: nowrap;
  cursor: pointer;
  background-color: #fff;
  border: 1px solid #fff;
  text-align: center;
  box-sizing: border-box;
  outline: none;
  font-weight: 500;
  line-height: 1;
  user-select: none;
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  min-width: 98px;
  padding: 12px 18px;
  font-size: 14px;
  border-radius: 4px;
  box-shadow: 0 4px 6px rgb(50 50 93 / 11%), 0 1px 3px rgb(0 0 0 / 8%);
  &:hover:not(:disabled) {
    box-shadow: 0 7px 14px rgb(50 50 93 / 10%), 0 3px 6px rgb(0 0 0 / 8%);
  }
}

.t-button > .button__inner {
  display: flex;
  align-items: center;
}

.t-button.t-button--small {
  min-width: 92px;
  padding: 10px 18px;
}
.t-button.t-button--mini {
  min-width: 80px;
  padding: 9px 15px;
  font-size: 12px;
  border-radius: 3px;
}
.t-button.t-button--icon {
  min-width: 40px;
  padding: 8px 15px;
  font-size: 18px;
  border-radius: 4px;
}
.t-button.is-round {
  box-sizing: border-box;
  border-radius: 20px;
}

.t-button.is-disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
.t-button__default {
  color: var(--t-primary);
  background-color: transparent;
  &:active:not(:disabled) {
    background: #e6e6e6;
    border-color: #e6e6e6;
    color: var(--t-default);
    box-shadow: 0 0 0 transparent;
  }
}

.t-button__primary {
  color: #fff;
  background-color: var(--t-primary);
  border-color: var(--t-primary);
  &:active:not(:disabled) {
    background: #324cdd;
  }
}
.t-button__success {
  color: #fff;
  background-color: var(--t-success);
  border-color: var(--t-success);
  &:active:not(:disabled) {
    background: #24a46d;
  }
}
.t-button__warning {
  color: #fff;
  background-color: var(--t-warning);
  border-color: var(--t-warning);
  &:active:not(:disabled) {
    background: #fa3a0e;
  }
}

.t-button__info {
  color: #fff;
  background-color: var(--t-info);
  border-color: var(--t-info);
  &:active:not(:disabled) {
    background: #0da5c0;
  }
}

.t-button__danger {
  color: #fff;
  background-color: var(--t-danger);
  border-color: var(--t-danger);
  &:active:not(:disabled) {
    background: #ec0c38;
  }
}

我们来在 examples 中测试一下看看

图标按钮

在此之前,我们需要准备好我们组件库的所有图标,建议使用 font class,这样我们可以根据指定类名来给按钮添加图表,我这边是在阿里巴巴矢量图库中找的,我们可以创建一个项目,找到自己想要的保存至自己项目,然后选择 Font class,下载至本地

我们将 font 文件放置 /packages/theme-chalk/fonts 中,然后在 /packages/theme-chalk/components/icon.less中使用,记得在样式的入口文件中导入 icon.less

less 复制代码
@font-face {
  font-family: "test-ui-icons"; /* Project id 4465617 */
  src: url("../fonts/iconfont.woff2") format("woff2"), url("../fonts/iconfont.woff") format("woff"),
    url("../fonts/iconfont.ttf") format("truetype");
}

.t-icon {
  font-family: "test-ui-icons" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  display: inline-block;
}

.icon-3column:before {
  content: "\e663";
}

.icon-column-4:before {
  content: "\e664";
}

我们回到 button 组件,添加 icon 属性,接收一个字符串,然后我们在 button 中创建一个 i 标签用于显示图标

js 复制代码
export const ButtonProps = {
  ...
  // 图标
  icon: {
    type: String,
    default: "",
  },
};
html 复制代码
<template>
  <button
    class="t-button"
    :class="[
      `${size && 't-button--' + size}`,
      `t-button__${type}`,
      { 't-button--icon': icon },
      { 'is-disabled': disabled },
      { 'is-round': round },
    ]"
    :disabled="disabled"
  >
    <div class="t-button__inner">
      <i v-if="icon" :class="['t-icon', `icon-${icon}`]"></i>
      <span v-if="$slots.default">
        <slot />
      </span>
    </div>
  </button>
</template>

我们写几个来看看效果

html 复制代码
<div>
  <t-button type="primary" icon="chart-bar">primary</t-button>
  <t-button type="success" icon="calendar">success</t-button>
  <t-button type="info" icon="data-view"></t-button>
  <t-button type="primary" icon="download"></t-button>
</div>

按钮 loading

loading 可以当做 icon 和 disabled 的结合,我们先定义 loading 属性

js 复制代码
export const ButtonProps = {
  ...
  // 加载
  loading: {
    type: Boolean,
    default: false,
  },
};
html 复制代码
<button
  class="t-button"
  :class="[
      `${size && 't-button--' + size}`,
      `t-button__${type}`,
      { 't-button--icon': icon },
      { 'is-disabled': disabled || loading },
      { 'is-round': round },
    ]"
  :disabled="disabled || loading"
>
  <div class="t-button__inner">
    <i v-if="loading" class="t-icon icon-loading"></i>
    <i v-if="icon" :class="['t-icon', `icon-${icon}`]"></i>
    <span v-if="$slots.default">
      <slot />
    </span>
  </div>
</button>
css 复制代码
@keyframes rotating {
  0% {
    transform: rotate(0);
  }

  to {
    transform: rotate(360deg);
  }
}

.t-button .icon-loading {
  animation: rotating 2s linear infinite;
}

本专栏源码地址

相关推荐
桂月二二2 分钟前
基于模块联邦的微前端架构:重构大型前端应用的模块化边界
前端·重构·架构
前端没钱38 分钟前
日报列表滚动到哪里、哪里就自动变成已读状态
前端·vue.js
bigyoung40 分钟前
告警 Detected multiple renderers concurrently rendering the same context provider
react.js·前端框架
想尝一尝被打赏的味道41 分钟前
uniapp在app下使用mqtt协议!!!支持vue3
javascript·vue.js·uni-app
lc_front_developer44 分钟前
为什么使用Knex 做为 Express 中操作 MySQL 数据库的方案?
前端·node.js
cc.ChenLy1 小时前
vue框架后遗症∶被遗忘的dom操作
javascript·vue.js
❆VE❆1 小时前
vue3: directive自定义指令防止重复点击
前端·javascript·vue.js·自定义指令·directive
布兰妮甜2 小时前
Fetch API 与 XMLHttpRequest:深入剖析异步请求的利器
前端·javascript·xmlhttprequest·fetch api
巴巴博一2 小时前
vue-i18n国际化插件安装教程(Vue3篇)
前端·javascript·vue.js·typescript