这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

本篇文章记录仿写一个el-button组件细节,从而有助于大家更好理解饿了么ui对应组件具体工作细节。本文是elementui源码学习仿写系列的又一篇文章,后续空闲了会不断更新并仿写其他组件。源码在github上,大家可以拉下来,npm start运行跑起来,结合注释有助于更好的理解

网站效果演示:ashuai.work:8888/#/myButton

GitHub仓库地址:github.com/shuirongshu…

什么是Button组件

按钮用于点击,一般是做事件的响应。

按钮封装效果图

按钮分类

  • 单一按钮

    • 默认按钮
    • 主题按钮(primary、success、warning、error)
    • 按钮大小(small、middle、big)
    • 按钮禁用(disabled)
    • 按钮加载(loading)
    • 按钮的图标位置(默认图标在按钮文字左侧)
    • 图标按钮(没有按钮文字)
    • 单一文字按钮
  • 按钮组(按钮组中有多个按钮)

默认按钮

默认按钮很简单,只是写一个最普通的样式即可

<button :class="[ 'myButton' ]" />

.myButton {
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
box-sizing: border-box;
padding: 12px 16px;
background-color: rgba(0, 0, 0, 0.1);
color: #222;
border: none;
cursor: pointer;
user-select: none; // 不让选中文字
transition: all 0.3s;
font-size: 14px;
}
// 悬浮效果
.myButton:hover {
background-color: rgba(0, 0, 0, 0.2);
}
// 按中效果
.myButton:active {
background-color: rgba(0, 0, 0, 0.3);
}

笔者这里是将悬浮的效果和按中的效果,设置背景色越来越深。这样的话,看着效果比较明显

主题按钮

所谓按钮的主题,就是添加不同的类名,比如primary主题的按钮,就加上.primary类名、success主题的按钮,就加上.success类名。然后使用动态class去添加即可(这里使用动态class的数组用法)。如:

<button :class="[ 'myButton', type ]" />

变量type的值源自于使用按钮组件时,传递进来的type参数

const typeArr = [
"",
"primary",
"success",
"warning",
"error",
"text",
"dangerText",
]; props:{
type: { // 按钮主题类型
type: String,
validator(val) {
return typeArr.includes(val); // 这里可以加一个校验函数,其实不加也行
},
},
}

然后给不同type值加上对应的样式即可。如下:

// primary样式
.primary {
background-color: #1867c0;
color: #fff;
}
.primary:hover {
background-color: #0854ac;
}
.primary:active {
background-color: #033d7f;
} // success样式
.success {
background-color: #19be6b;
color: #fff;
}
.success:hover {
background-color: #0ea459;
}
.success:active {
background-color: #008140;
} // warning样式
.warning {
background-color: #ffc163;
color: #fff;
}
.warning:hover {
background-color: #db952d;
}
.warning:active {
background-color: #b97b1d;
} // 等等type值样式...

按钮大小

按钮大小可以使用padding值的大小去控制,也可以直接使用zoom缩放做控制

这里使用动态style搭配计算属性的方式去控制,如下代码:

// 不同的大小指定不同的缩放程度
const sizeObj = {
small: 0.85,
middle: 1,
big: 1.2,
}; props:{ size: String } <button :style="styleCal" /> computed: {
styleCal() {
return {
zoom: sizeObj[this.size] // zoom缩放的值大小取决于传递进来的size值
}
}
}

按钮禁用

按钮禁用disable没啥好说的,主要是要注意loading的时候,也要禁用掉,loading加载的时候,不允许用户再点击。

<button :disabled="disabled || loading" />

props:{
loading:Boolean
}

这里注意一下,按钮禁用的样式也是通过动态class加上的,请往下看

按钮加载

注意加载时样式和加载按钮图标出来的时候,将其他的图标给隐藏起来。(同一时刻,只能有一个按钮图标,这样保证按钮加载时简洁一些)

  <button
:class="[
'myButton', // 默认样式
disabled ? 'disabledBtn' : '', // 动态加上禁用按钮样式
loading ? 'loadingBtn' : '', // 动态加上loading加载中按钮样式
type, // 主题样式
]"
:disabled="disabled || loading" // 禁用时禁用,加载时也禁用
>
<i class="el-icon-loading" v-if="loading"></i>
<!-- 使用传进来的图标,通过动态style控制图标和文字见的间隔,同一时刻下,
只能有一个图标出现,所以有loading图标了,就不能有别的图标了 -->
<i :class="icon" :style="styleGap" v-if="icon && !loading"></i>
<slot></slot>
</button>

按钮的图标位置

默认从左往右排列(图标在左侧、文字在右侧),这里我们可以使用弹性盒的方向flexDirection属性,来控制从左往右还是从右往左排列

<button :style="styleCal"/>

styleCal() {
// 控制缩放和指定默认圆角以及设置图标在文字左侧还是右侧
let styleObj = {
zoom: sizeObj[this.size],
borderRadius: "5px",
flexDirection: this.rightIcon ? "row-reverse" : "row",
};
return styleObj;
},

图标按钮和单一文字按钮

这两个也很简单,

  • 图标按钮注意加圆角的时机
  • 单一文字按钮的样式要预留设置一份即可

然后动态控制一下即可

按钮组

按钮组注意事项:

  • 首先将所有的按钮的圆角全部去掉(这样的话,所有的按钮都是方方正正的按钮了)
  • 然后单独给第一个按钮:first-of-type的左上角和左下角的圆角设置一下
  • 然后再给最后一个按钮last-of-type的右上角和右下角的圆角设置一下
  • 最后,按钮组之间需要有间隔,这里使用border-right做分割线
  • 最最后,再把最后一个按钮的右边框去掉即可,如下css代码
// 附上按钮组样式
.myButtonGroup > .myButton {
border-radius: unset !important; // 给所有的按钮都去掉圆角
border-right: 1px solid #fff; // 给按钮加上分隔线条
}
// 第一个按钮左侧圆角
.myButtonGroup > .myButton:first-of-type {
border-top-left-radius: 5px !important;
border-bottom-left-radius: 5px !important;
}
// 最后一个按钮的右侧圆角
.myButtonGroup > .myButton:last-of-type {
border-top-right-radius: 5px !important;
border-bottom-right-radius: 5px !important;
border-right: none; // 同时,清除最后一个按钮的右侧边框
}

代码

复制粘贴即可使用,如果道友觉得代码帮忙到了您,欢迎给咱github仓库一个star哈

myButton组件

<template>
<button
:style="styleCal"
:class="[
'myButton',
disabled ? 'disabledBtn' : '',
loading ? 'loadingBtn' : '',
type,
]"
:disabled="disabled || loading"
@click="clickButton"
>
<i class="el-icon-loading iii" v-if="loading"></i>
<!-- 使用传进来的图标,通过动态style控制图标和文字见的间隔,同一时刻下,
只能有一个图标出现,所以有loading图标了,就不能有别的图标了 -->
<i :class="icon" :style="styleGap" v-if="icon && !loading"></i>
<!-- 普通插槽有东西才去渲染 -->
<span v-if="$slots.default"><slot></slot></span>
</button>
</template> <script>
// 类型校验
const typeArr = [
"",
"primary",
"success",
"warning",
"error",
"text",
"dangerText",
];
const sizeArr = ["", "small", "middle", "big"]; // 大小检验
const sizeObj = {
// 不同的大小指定不同的缩放程度
small: 0.85,
middle: 1,
big: 1.2,
};
export default {
name: "myButton",
props: {
disabled: Boolean,
loading: Boolean, // loading时,不可继续点击(继续点击不生效)
rightIcon: Boolean, // 通过弹性盒的方向控制图标的位置
type: {
type: String,
validator(val) {
return typeArr.includes(val);
},
},
size: {
type: String,
validator(val) {
return sizeArr.includes(val);
},
},
icon: String,
},
computed: {
styleCal() {
// 控制缩放和指定默认圆角以及设置图标在文字左侧还是右侧
let styleObj = {
zoom: sizeObj[this.size],
borderRadius: "5px",
flexDirection: this.rightIcon ? "row-reverse" : "row",
};
// 当有图标,且没有文字的时候(或默认插槽没传),就让按钮变成圆形按钮
if ((this.icon && !this.$slots.default) || !this.$slots.default[0].text) {
styleObj["borderRadius"] = "50%";
styleObj["padding"] = "12px";
}
return styleObj;
},
styleGap() {
// 有图标,有文字,图标在左侧
if (
(this.icon && !this.$slots.default) ||
(this.$slots.default[0].text && !this.rightIcon)
) {
return {
paddingRight: "1px",
};
}
// 有图标,有文字,图标在右侧
if (
(this.icon && !this.$slots.default) ||
(this.$slots.default[0].text && this.rightIcon)
) {
return {
paddingLeft: "1px",
};
}
},
},
methods: {
clickButton(e) {
if (this.disabled) return;
this.$emit("click", e); // 传出去,便于使用
},
},
};
</script> <style lang='less' scoped>
/* 关于按钮的样式即写好几套样式,然后通过类型等各种参数去控制样式,最终实现对应效果 */ // 基础样式
.myButton {
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
box-sizing: border-box;
padding: 12px 16px;
background-color: rgba(0, 0, 0, 0.1);
color: #222;
border: none;
cursor: pointer;
user-select: none;
transition: all 0.3s;
font-size: 14px;
.iii {
margin-right: 4px;
}
}
.myButton:hover {
background-color: rgba(0, 0, 0, 0.2);
}
.myButton:active {
background-color: rgba(0, 0, 0, 0.3);
} // primary样式
.primary {
background-color: #1867c0;
color: #fff;
}
.primary:hover {
background-color: #0854ac;
}
.primary:active {
background-color: #033d7f;
} // success样式
.success {
background-color: #19be6b;
color: #fff;
}
.success:hover {
background-color: #0ea459;
}
.success:active {
background-color: #008140;
} // warning样式
.warning {
background-color: #ffc163;
color: #fff;
}
.warning:hover {
background-color: #db952d;
}
.warning:active {
background-color: #b97b1d;
} // error样式
.error {
background-color: #ff5252;
color: #fff;
}
.error:hover {
background-color: #fd3030;
}
.error:active {
background-color: #d50000;
} // text样式
.text {
background-color: unset;
color: #409eff;
padding: 2px 4px;
}
.text:hover {
background-color: unset;
opacity: 0.9;
}
.text:active {
background-color: unset;
opacity: 1;
color: #1a7ada;
} // dangerText样式
.dangerText {
background-color: unset;
color: #ff5252;
padding: 2px 4px;
}
.dangerText:hover {
background-color: unset;
opacity: 0.9;
}
.dangerText:active {
background-color: unset;
opacity: 1;
color: #d50000;
} // 加载按钮样式
.loadingBtn {
opacity: 0.6;
pointer-events: none; // 值为none就没有hover和active效果了
} // disabled样式(注意样式的顺序)
.disabledBtn {
background-color: rgba(0, 0, 0, 0.12);
color: #bbb;
}
.disabledBtn:hover {
opacity: 1;
cursor: not-allowed;
background-color: rgba(0, 0, 0, 0.12);
}
.disabledBtn:active {
color: #bbb;
opacity: 1;
background-color: rgba(0, 0, 0, 0.12);
} // 附上按钮组样式
.myButtonGroup > .myButton {
border-radius: unset !important;
border-right: 1px solid #fff;
}
.myButtonGroup > .myButton:first-of-type {
border-top-left-radius: 5px !important;
border-bottom-left-radius: 5px !important;
}
.myButtonGroup > .myButton:last-of-type {
border-top-right-radius: 5px !important;
border-bottom-right-radius: 5px !important;
border-right: none;
}
</style>

myButtonGroup组件

<template>
<div class="myButtonGroup">
<slot></slot>
</div>
</template>
<script>
export default {
name: "myButtonGroup",
};
</script>
<style>
.myButtonGroup {
display: inline-flex !important;
align-items: center;
}
</style>

使用的时候

<template>
<div>
<h5>单个按钮</h5>
<br />
<button @click="clickLoad">加载切换</button>
<div class="btnBox">
<span class="btn" v-for="(item, index) of btnArr">
<my-button
style="margin-right: 16px"
:key="index"
:type="item.type"
:size="item.size"
:disabled="item.disabled"
:loading="item.loading"
:icon="item.icon"
:rightIcon="item.rightIcon"
@click="
(e) => {
clickBtn(item, e);
}
"
>{{ item.name }}</my-button
>
</span>
</div>
<br />
<h5>按钮组</h5>
<br />
<my-button-group>
<my-button type="success" icon="el-icon-arrow-left">上一页</my-button>
<my-button type="success" icon="el-icon-arrow-right" :rightIcon="true"
>下一页</my-button
>
</my-button-group>
<br />
<br />
<my-button-group>
<my-button type="primary" icon="el-icon-user"></my-button>
<my-button type="primary" icon="el-icon-view"></my-button>
<my-button type="primary" icon="el-icon-star-off"></my-button>
<my-button type="primary" icon="el-icon-chat-dot-square"></my-button>
<my-button type="primary" icon="el-icon-share"></my-button>
</my-button-group>
</div>
</template> <script>
export default {
name: "myButtonName",
data() {
return {
loadingF: false,
btnArr: [
{
type: "",
name: "默认按钮",
},
{
type: "primary",
name: "primary",
},
{
type: "success",
name: "success",
},
{
type: "warning",
name: "warning",
},
{
type: "error",
name: "error",
},
{
type: "primary",
name: "size=small",
size: "small",
},
{
type: "primary",
name: "size=middle",
size: "middle",
},
{
type: "primary",
name: "size=big",
size: "big",
},
{
type: "success", // 不管type什么类型,只要禁用全部置灰
name: "disabled",
disabled: true,
},
{
type: "primary",
name: "等待加载",
loading: false,
},
{
type: "success",
name: "等待加载",
loading: false,
},
{
type: "success",
name: "icon",
icon: "el-icon-star-on",
},
{
type: "success",
name: "icon",
icon: "el-icon-star-on",
rightIcon: true,
},
{
type: "success",
name: "",
icon: "el-icon-edit",
},
{
type: "error",
name: "",
icon: "el-icon-delete",
},
{
type: "text",
name: "纯text按钮",
// loading: true,
},
{
type: "dangerText",
name: "dangerText按钮",
icon: "el-icon-delete-solid",
},
{
type: "text",
name: "text禁用",
disabled: true,
},
],
};
},
methods: {
clickLoad() {
let lebel = this.btnArr[9].name;
let newItem9 = {
type: "primary",
name: lebel == "等待加载" ? "加载中" : "等待加载",
loading: lebel == "等待加载" ? true : false,
};
this.$set(this.btnArr, 9, newItem9);
let newItem10 = {
type: "success",
name: lebel == "等待加载" ? "加载中" : "等待加载",
loading: lebel == "等待加载" ? true : false,
};
this.$set(this.btnArr, 10, newItem10);
},
// 注意这种写法,可接收多个参数
clickBtn(item, e) {
console.log("clickBtn", item, e);
},
},
};
</script> <style>
.btnBox {
width: 100%;
box-sizing: border-box;
padding: 24px 0;
display: flex;
align-items: flex-end;
flex-wrap: wrap;
}
.btn {
margin-bottom: 24px;
}
</style>

本文转载于:

https://juejin.cn/post/7182113902539309112

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

记录--elementui源码学习之仿写一个el-button的更多相关文章

  1. 05.ElementUI源码学习:项目发布配置(github pages&npm package)

    0x00.前言 书接上文.项目第一个组件已经封装好,说明文档也已编写好.下面需要将说明文档发布到外网上,以此来展示和推广项目,使用 Github Pages功能实现.同时将组件发布之 npm 上,方便 ...

  2. 02.ElementUI源码学习:babel配置

    书接上文,接下来项目将引入babel支持ES6+语法兼容. Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行 ...

  3. 04.ElementUI源码学习:组件封装、说明文档的编写发布

    0x00.前言 书接上文.项目经过一系列的配置,开发脚手架已经搭建完毕.接下来开始封装自定义组件.并基于 markdown 文件生成文档和演示案例. 后续文章代码会根据篇幅,不影响理解的情况下进行部分 ...

  4. 03.ElementUI源码学习:代码风格检查和格式化配置(ESlint & Prettier)

    书接上文.在团队协作中,为避免低级Bug.以及团队协作时不同代码风格对彼此造成的困扰与影响,会预先制定编码规范.使用 Lint工具和代码风格检测工具,则可以辅助编码规范执行,格式化代码,使样式与规则保 ...

  5. element-ui 源码学习

    https://athena0304.github.io/element-analysis/ 1.模板字符串实现字符串拼接 typeClass() { return `el-alert--${ thi ...

  6. 带着萌新看springboot源码13(手写一个自己的starter)

    springboot的最强大的就是那些xxxAutoconfiguration,但是这些xxxAutoConfiguration又依赖那些starter,只有导入了这些场景启动器(starter),我 ...

  7. jQuery源码学习一: 创建一个jquery实例

    前言: jquery是每个前端都会的基础技能,众所周知,jquery返回的是jquery实例方法,但是我们似乎是直接使用$就可以获取到jquery的方法啦,可以在浏览器中判断一下 window.$ 和 ...

  8. 06.ElementUI 2.X 源码学习:源码剖析之工程化(一)

    0x.00 前言 在用了5章篇幅 ElementUI源码学习:从零开始搭建Vue组件库汇总 讲解了如何编写一个组件.发布npm以及生成展示文档之后.接下来将分析Element项目的代码结构,学习其工程 ...

  9. 07.ElementUI 2.X 源码学习:源码剖析之工程化(二)

    0x.00 前言 项目工程化系列文章链接如下,推荐按照顺序阅读文章 . 1️⃣ 源码剖析之工程化(一):项目概览.package.json.npm script 2️⃣ 源码剖析之工程化(二):项目构 ...

  10. 08.ElementUI 2.X 源码学习:源码剖析之工程化(三)

    0x.00 前言 项目工程化系列文章链接如下,推荐按照顺序阅读文章 . 1️⃣ 源码剖析之工程化(一):项目概览.package.json.npm script 2️⃣ 源码剖析之工程化(二):项目构 ...

随机推荐

  1. NVM Feature— Reservation(NVME 学习笔记五)

    8.8 Reservations 预订 NVMe的reservation预订功能,用于让两个或多个主机能够协调配合的访问共享namespace.使用这些功能的协议和方式超出了本规格说明书的范围.对这些 ...

  2. Java设计模式-状态模式State

    介绍 状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题.状态和行为是一一对应的,状态之间可以相互转换. 当一个对象的内在状态改变时,允许改变其行 ...

  3. Vue+SpringBoot+ElementUI实战学生管理系统-8.班级管理模块

    1.章节介绍 前一篇介绍了专业管理模块,这一篇编写班级管理模块,需要的朋友可以拿去自己定制.:) 2.获取源码 源码是捐赠方式获取,详细请QQ联系我 :)! 3.实现效果 班级列表 修改班级 4.模块 ...

  4. Java I/O 教程(七) DataOutputStream和DataInputStream

    Java DataOutputStream Class Java DataOutputStream class 可以以机器无关方式往指定输出流写入Java原始数据类型,例如int, double, l ...

  5. django中使用celery异步发送邮件

    申请163网易发送邮件权限 在django中settings配置文件 #配置邮件服务器 EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBac ...

  6. jdk17新特性梳理

    jdk17新特性梳理 目录 jdk17新特性梳理 jdk8升级至jdk17新特性梳理 升级jdk17的理由 新特性梳理 可以在接口中定义私有方法,主要为了jdk8的default方法 局部变量可以使用 ...

  7. 【Azure 应用服务】App Service 默认开放端口说明, 如何禁用Web app的端口号? 

    问题描述 基于安全的角度来考虑,在网站上线之前用户会对自己的网站进行安全扫描,以防网站因为某些漏洞而被非法攻击. 而在扫描过程中,会发现除了 80 和 443 之外的一些其他端口也被开放了.例如:45 ...

  8. 【Azure Developer】使用 Azure VM 上的用户分配托管标识访问 Azure Key Vault 中国区代码示例

    问题描述 在Global版本的Azure Key Vault 文档中,有一节介绍在Azure VM中使用标识获取访问令牌,调用Key Vault中的资源.但是在示例中,只有curl的命令执行,而没有代 ...

  9. 【Azure Developer】如何通过Azure REST API 获取到虚拟机(VM)所使用的公共IP地址信息

    问题描述 如何通过Azure REST API 获取到虚拟机(VM)所使用的公共IP地址信息 问题解答 由于直接获取到的虚拟机信息(Virtual Machines - Get)中,并不会包含虚拟机的 ...

  10. SQL之基本查询

    提纲 记录查询 使用列别名 查询语句执行顺序 数据分页 子句执行顺序 结果集排序 PS: 排序注意 多个排序字段 子句执行顺序 结果集去除重复数据 注意: 条件查询 比较运算符 注意 子句执行顺序