模仿element-ui封装vue组件库(button)
作者:互联网
实例内容
封装常见的功能组件(Button,Modal,Form相关),封装完成后封装成UI组件库发布到NPM上。
涉及知识点
vue基础语法
组件基本语法
字键通讯(sync,provide,inject)
插槽使用
prop校验
过渡与动画处理
计算属性与监听属性
v-model语法糖
vue插件机制
npm发布
实例目的
掌握组件封装的语法和技巧学会造轮子,了解组件库实现原理搭建和积累自己的组件库学习前提有一定vue基础,懂vue语法熟悉ES6的一些常见语法对vue感兴趣
----------------------------------------------------------------------------分割线---------------------------------------------------------------------------------
一、使用vue脚手架初始化一个项目
使用vue created rainbow-ui,创建一个名为rainbow-ui的项目。
按照自己的习惯设置脚手架风格,这里不多做介绍。
脚手架搭建完毕后,将App.vue文件下的自带内容清理一下,为后续开发做准备。
二、如何封装,注册和使用一个组件
在componet下创建一个button.vue的文件,放置button组件代码。创建一个组建的button组件,,并且指定name为RaButton。
<template> <button class="ra-button"> 按钮组件 </button> </template> <script> export default { name: 'ra-button' } </script> <style lang="scss"> </style>
创建组件完成后,不能在项目中直接使用,需要到main.js中注册才可以使用。
import Vue from 'vue' import App from './App.vue' // 第一步:导入button组件 import RaButton from './components/button.vue' Vue.config.productionTip = false // 第二步:注册组件,设置(组件名,组件) Vue.component(RaButton.name, RaButton) new Vue({ render: h => h(App) }).$mount('#app') 注册完成后,组件就可以在项目中使用了。 <template> <div> <ra-button></ra-button> </div> </template>
按钮效果:
组件最简单的封装,注册和使用方法就是这样一个流程。
三、封装一个element-ui风格的按钮
需要使用到的知识:
组件通讯
组件插槽
props校验
参数支持:
参数名 |
参数描述 | 参数类型 | 默认值 |
type | 按钮类型(primary/success/warning/danger/info) | string | default |
plain | 是否为朴素按钮 | boolean | false |
round | 是否圆角按钮 | boolean | false |
circle | 是否圆形按钮 | boolean | false |
disabled | 是否禁用 | boolean | false |
icon | 图标类型 | string | 无 |
事件支持:
事件名 |
事件描述 |
click | 点击事件 |
使用插槽:
简单来说,凡是希望组件中内容可以灵活设置的地方,都需要用到slot插槽来自定义内容。
使用slot来定义按钮上的文本内容:
<template> <button class="-button"> <span><slot></slot></span> </button> </template> 在使用时就可以直接输入文本,定义按钮文本内容了: <template> <div> <ra-button>登录</ra-button> <ra-button>删除</ra-button> <ra-button>取消</ra-button> </div> </template>
效果:
button组件基础样式:
<style lang="scss"> .ra-button{ display: inline-block; line-height: 1; white-space: nowrap; cursor: pointer; background: #ffffff; border: 1px solid #dcdfe6; color: #606266; -webkit-appearance: none; text-align: center; box-sizing: border-box; outline: none; margin: 0; transition: 0.1s; font-weight: 500; //禁止元素的文字被选中 -moz-user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; padding: 12px 20px; font-size: 14px; border-radius: 4px; &:hover, &:hover{ color: #409eff; border-color: #c6e2ff; background-color: #ecf5ff; } } </style>
效果:
3.1button组件的type属性
让按钮支持type属性,使得按钮支持不同样式:
第一步:父组件组件传递type属性
<div id="app"> <div class="row"> <ra-button plain>按钮</ra-button> <ra-button plain type="primary">primary按钮</ra-button> <ra-button plain type="success">success按钮</ra-button> <ra-button plain type="info">info按钮</ra-button> <ra-button plain type="danger">danger按钮</ra-button> <ra-button plain type="warning">warning按钮</ra-button> </div> </div>
第二步:子组件接收负组件传递的数据
export default { name: 'ra-button', // 此时对props进行校验,值接收string类型的type值 props: { type:{ type: String, // 设置默认值:如果不传值,那么使用default default: 'default' } }, created () { console.log(this.type)//defalut primary success info danger warning } }
此时对type进行校验,如果传递了非String类型的值(这里我传递了123),将会报错。
failed for prop "type". Expected String with value "123", got Number with value 123.
第三步:通过绑定类名的方法动态控制样式
<template> <button class="ra-button" :class="`ra-button-${type}`"> <span><slot></slot></span> </button> </template>
第四步:设置不同类型的样式
.ra-button-primary{ color:#fff; background-color: #409eff; border-color: #409eff; &:hover, &:focus{ background: #66b1ff; background-color: #66b1ff; color: #fff; } } .ra-button-success{ color:#fff; background-color: #67c23a; border-color: #67c23a; &:hover, &:focus{ background: #85ce61; background-color: #85ce61; color: #fff; } } .ra-button-info{ color:#fff; background-color: #909399; border-color: #909399; &:hover, &:focus{ background: #a6a9ad; background-color: #a6a9ad; color: #fff; } } .ra-button-warning{ color:#fff; background-color: #e6a23c; border-color: #e6a23c; &:hover, &:focus{ background: #ebb563; background-color: #ebb563; color: #fff; } } .ra-button-danger{ color:#fff; background-color: #f56c6c; border-color: #f56c6c; &:hover, &:focus{ background: #f78989; background-color: #f78989; color: #fff; } }
第五步:至此就完成了对于按钮样式的设置,查看一下效果
3.2button组件的plain属性
和type类型相同,我们只要将样式先设置好,然后通过父组件传递过来的值进行判断,就可以设置plain属性了。
第一步:父组件组件传递plain值
<template> <div id="app"> <div class="row"> <ra-button plain>按钮</one-button> <ra-button plain type="primary">primary按钮</ra-button> <ra-button plain type="success">success按钮</ra-button> <ra-button plain type="info">info按钮</ra-button> <ra-button plain type="danger">danger按钮</ra-button> <ra-button plain type="warning">warning按钮</ra-button> </div> </div> </template>
第二步:子组件接收负组件传递的数据,同样进行props校验,并且设置默认值为false
props: { plain: { type: Boolean, default: false } }
第三步:通过绑定类名的方法动态控制样式,由于plain类型是布尔值,所以在类型中我们使用对象的形式来控制样式
<template> <button class="ra-button" :class="[`ra-button-${type}`,{ 'is-plain':plain }]"> <span><slot></slot></span> </button> </template>
第四步:设置不同类型的样式,由于plain类型是以对象的形式在类中定义的,所以使用获取属性的方法定义样式
// 朴素按钮样式 .ra-button.is-plain{ &:hover, &:focus{ background: #fff; border-color: #489eff; color: #409eff; } } .ra-button-primary.is-plain{ color: #409eff; background: #ecf5ff; &:hover, &:focus{ background: #409eff; border-color: #409eff; color: #fff; } } .ra-button-success.is-plain{ color: #67c23a; background: #c2e7b0; &:hover, &:focus{ background: #67c23a; border-color: #67c23a; color: #fff; } } .ra-button-info.is-plain{ color: #909399; background: #d3d4d6; &:hover, &:focus{ background: #909399; border-color: #909399; color: #fff; } } .ra-button-warning.is-plain{ color: #e6a23c; background: #f5dab1; &:hover, &:focus{ background: #e6a23c; border-color: #e6a23c; color: #fff; } } .ra-button-danger.is-plain{ color: #f56c6c; background: #fbc4c4; &:hover, &:focus{ background: #f56c6c; border-color: #f56c6c; color: #fff; } }
第五步:至此就完成了对于按钮样式的设置,查看一下效果
3.3button组件的round属性
设置round属性和之前的相似,只要在组件中定义好了样式,动态获取属性值即可。
获取属性值:
round: { type: Boolean, default: false }
round样式:
.ra-button.is-round{ border-radius: 20px; padding: 12px 23px; }
效果图:
3.4button组件的circle属性
circle同样是上面的方法,样式为:
.ra-button.is-circle{ border-radius: 50%; padding: 12px; }
3.5button组件中使用字体图标
在项目中使用字体图标,首先需要有字体图标,我们可以去阿里巴巴矢量图标库下载。
下载完成后,在asset目录下新建一个fonts目录,存放我们下载到的字体图标。
做完准备工作后,我们就可以开始把字体图标运用到项目中了。
第一步:在main.js中引入字体图标
import './assets/fonts/iconfont.css'
第二步:将下载的字体图标css文件中的类名做修改,我将icon全部改为了ra-icon,并且将初始的iconfont类改为了[class*='ra-icon'],当类名中有ra-icon时使用,如下
[class*='ra-icon'] { font-family: "iconfont" !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .ra-icon-bluetoothoff:before { content: "\e697"; }
第三步:父组件传递图标名,子组件接收并且放到图标中
父组件传值:
<div class="row"> <ra-button plain icon='ra-icon-delete' circle></ra-button> <ra-button plain icon='ra-icon-delete' circle type="primary"></ra-button> <ra-button plain icon='ra-icon-delete' circle type="success"></ra-button> <ra-button plain icon='ra-icon-delete' circle type="info"></ra-button> <ra-button plain icon='ra-icon-message' circle type="danger"></ra-button> <ra-button plain icon='ra-icon-check' circle type="warning"></ra-button> </div>
子组件接收:
icon: { type: String, default: '' }
使用接收到的字体图标。在没有传入icon时隐藏<i>标签,在slot插槽没有传入值时,不显示<span>标签
<template> <button class="ra-button" :class="[`ra-button-${type}`,{ 'is-plain':plain, 'is-round':round, 'is-circle':circle, }]"> <i v-if="icon" :class="`ra-icon-${icon}`"></i> <!-- 如果没传入文本插槽,则不显示span内容 --> <span v-if="$slots.default"><slot></slot></span> </button> </template>
第四步:设置icon配套样式,使图标和文字之间有一定间隔
.ra-button [class*=ra-icon-]+span{ margin-left: 5px; }
第五步:查看效果
3.6button组件中的点击事件
我们在使用组件时,直接给组件定义事件是不会被触发的。我们需要在组件中定义一个点击事件,这个点击事件不进行其他操作,只出发父组件中的点击事件。
组件中的定义点击事件:
<template> <button class="ra-button" :class="[`ra-button-${type}`,{ 'is-plain':plain, 'is-round':round, 'is-circle':circle, }]" @click="handleClick" > <i v-if="icon" :class="`ra-icon-${icon}`"></i> <!-- 如果没传入文本插槽,则不显示span内容 --> <span v-if="$slots.default"><slot></slot></span> </button> </template>
定义一个点击事件,这个点击事件的作用是调用父组件中的点击事件,并且回调
methods: { handleClick (e) { this.$emit('click', e) } }
父组件在使用时定义自己的点击事件,其本质是子组件中的点击事件触发父组件中的点击事件。
<div class="row"> <ra-button @click="getInfo">按钮</ra-button> </div> methods: { getInfo () { console.log('获取信息!!')//获取信息!! } }
3.7button组件中的disabled属性
和之前相似,只要父子组件传值并且动态获取这个值并且赋给disabled属性,并且设置一个disabled样式即可。
<div class="row"> <ra-button @click="getInfo" disabled>按钮</ra-button> </div> <template> <button class="ra-button" :class="[`ra-button-${type}`,{ 'is-plain':plain, 'is-round':round, 'is-circle':circle, 'is-disabled':disabled }]" @click="handleClick" :disabled="disabled" > <i v-if="icon" :class="`ra-icon-${icon}`"></i> <span v-if="$slots.default"><slot></slot></span> </button> </template> disabled: { type: Boolean, default: false }
disabled样式:
.ra-button.is-disabled{ cursor: no-drop; }
------------------------------------------------------------------至此,按钮组件封装完成!------------------------------------------------------------------
附组件代码:
<template> <button class="ra-button" :class="[`ra-button-${type}`,{'is-plain':plain,'is-round':round,'is-circle':circle,'is-disabled':disabled}]" @click="handleClick" :disabled="disabled" > <i v-if="icon" :class="icon"></i> <!-- 如果没传入文本插槽,则不显示span内容 --> <span v-if="$slots.default"> <slot></slot> </span> </button> </template> <script> export default { name: 'ra-button', props: { type: { type: String, // 设置默认值:如果不传值,那么使用default default: 'default' }, plain: { type: Boolean, default: false }, round: { type: Boolean, default: false }, circle: { type: Boolean, default: false }, icon: { type: String, default: '' }, disabled: { type: Boolean, default: false } }, methods: { handleClick (e) { this.$emit('click', e) } } } </script> <style lang="scss" scope> .ra-button{ display: inline-block; line-height: 1; white-space: nowrap; cursor: pointer; background: #ffffff; border: 1px solid #dcdfe6; color: #606266; -webkit-appearance: none; text-align: center; box-sizing: border-box; outline: none; margin: 0; transition: 0.1s; font-weight: 500; //禁止元素的文字被选中 -moz-user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; padding: 12px 20px; font-size: 14px; border-radius: 4px; &:hover, &:hover{ color: #409eff; border-color: #c6e2ff; background-color: #ecf5ff; } } .ra-button-primary{ color:#fff; background-color: #409eff; border-color: #409eff; &:hover, &:focus{ background: #66b1ff; background-color: #66b1ff; color: #fff; } } .ra-button-success{ color:#fff; background-color: #67c23a; border-color: #67c23a; &:hover, &:focus{ background: #85ce61; background-color: #85ce61; color: #fff; } } .ra-button-info{ color:#fff; background-color: #909399; border-color: #909399; &:hover, &:focus{ background: #a6a9ad; background-color: #a6a9ad; color: #fff; } } .ra-button-warning{ color:#fff; background-color: #e6a23c; border-color: #e6a23c; &:hover, &:focus{ background: #ebb563; background-color: #ebb563; color: #fff; } } .ra-button-danger{ color:#fff; background-color: #f56c6c; border-color: #f56c6c; &:hover, &:focus{ background: #f78989; background-color: #f78989; color: #fff; } } // 朴素按钮样式 .ra-button.is-plain{ &:hover, &:focus{ background: #fff; border-color: #489eff; color: #409eff; } } .ra-button-primary.is-plain{ color: #409eff; background: #ecf5ff; &:hover, &:focus{ background: #409eff; border-color: #409eff; color: #fff; } } .ra-button-success.is-plain{ color: #67c23a; background: #c2e7b0; &:hover, &:focus{ background: #67c23a; border-color: #67c23a; color: #fff; } } .ra-button-info.is-plain{ color: #909399; background: #d3d4d6; &:hover, &:focus{ background: #909399; border-color: #909399; color: #fff; } } .ra-button-warning.is-plain{ color: #e6a23c; background: #f5dab1; &:hover, &:focus{ background: #e6a23c; border-color: #e6a23c; color: #fff; } } .ra-button-danger.is-plain{ color: #f56c6c; background: #fbc4c4; &:hover, &:focus{ background: #f56c6c; border-color: #f56c6c; color: #fff; } } .ra-button.is-round{ border-radius: 20px; padding: 12px 23px; } .ra-button.is-circle{ border-radius: 50%; padding: 12px; } .ra-button.is-disabled{ cursor: no-drop; } .ra-button [class*=ra-icon-]+span{ margin-left: 5px; } </style>
标签:vue,color,button,element,ra,background,组件,border 来源: https://www.cnblogs.com/Rainbow5421/p/16305174.html