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

本篇文章记录仿写一个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. C++——异常处理模块笔记

    异常处理是C++中的重要概念之一,用于处理在程序执行过程中可能发生的错误或异常情况.异常是指在程序执行过程中发生的一些不寻常的事件,例如除零错误.访问无效内存等.C++提供了一套异常处理机制,使得程序 ...

  2. XTW100编程器在Win10下的安装

    XTW100 这是一个淘宝上卖得很多的经典编程器, 用于写入24和25系列的存储芯片. 最初使用的是stm32f103c8t6, 因为f103价格飞涨, 市面上大都换成国产的兼容mcu了, 软件和使用 ...

  3. np.newaxis的用法

    1 前言 np.newaxis的意思是给数组新增一个维度."python中矩阵切片维数微秒变化"中介绍了矩阵切片有时候会降低矩阵维度,为保证维度不变,可以用np.newaxis新增 ...

  4. Context与Reducer

    Context与Reducer Context是React提供的一种跨组件的通信方案,useContext与useReducer是在React 16.8之后提供的Hooks API,我们可以通过use ...

  5. Js中Number对象

    Js中Number对象 JavaScript的Number对象是经过封装从而能够处理数字值的对象,Number对象由Number()构造器以及字面量声明的值在转化为包装对象时创建,JavaScript ...

  6. Laravel入坑指南(10)——事件Event

    不知不觉,我们已经来到了第10小节.这一小节,我们一起讨论关于"事件"这个话题.众所周知,从二进制到汇编,再到高等级语言,这一路发展下来,代码都是顺序执行的,那么事件是什么?这个事 ...

  7. Spring Boot图书管理系统项目实战-3.用户登录

    导航: pre:  2.项目搭建 next:4.基础信息管理 只挑重点的讲,具体的请看项目源码. 1.项目源码 需要源码的朋友,请捐赠任意金额后留下邮箱发送:) 2.登录页设计 <!DOCTYP ...

  8. 利用BARK和Telebot进行VPS实时预警推送

    前言 在服务器的日常维护和蓝队的日常监控中,经常需要对服务器出现的各种问题进行及时的预警推送.国外的服务器推荐使用telebot,而国内由于特殊的网络环境,则推荐使用BARK.Chanify等进行推送 ...

  9. linux下python3环境安装(源码编译的方式安装)

    1.将压缩包上传到/usr/local/ 2.解压压缩包 tar -xzvf Python-3.6.6.tgz 3.装一些编译源码需要的依赖文件 yum -y install gcc make cma ...

  10. 【LeetCode回溯算法#11】解数独,这次是真的用回溯法处理二维数组

    解数独 力扣题目链接(opens new window) 编写一个程序,通过填充空格来解决数独问题. 一个数独的解法需遵循如下规则: 数字 1-9 在每一行只能出现一次. 数字 1-9 在每一列只能出 ...