element-ui button组件 radio组件源码分析整理笔记(一)
Button组件
button.vue
<template>
<button
class="el-button"
@click="handleClick"
:disabled="buttonDisabled || loading"
:autofocus="autofocus"
:type="nativeType"
:class="[
type ? 'el-button--' + type : '',
buttonSize ? 'el-button--' + buttonSize : '',
{
'is-disabled': buttonDisabled,
'is-loading': loading,
'is-plain': plain,
'is-round': round,
'is-circle': circle
}
]"
>
<i class="el-icon-loading" v-if="loading"></i>
<i :class="icon" v-if="icon && !loading"></i>
<span v-if="$slots.default"><slot></slot></span>
</button>
</template>
<script>
export default {
name: 'ElButton',
//provider/inject:简单的来说就是在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量
inject: {
elForm: {
default: ''
},
elFormItem: {
default: ''
}
},
props: {
type: { //按钮类型
type: String,
default: 'default'
},
size: String, //按钮尺寸
icon: { //图标类名
type: String,
default: ''
},
nativeType: { //原生type类型
type: String,
default: 'button'
},
loading: Boolean,//是否加载中状态
disabled: Boolean,//是否禁用状态
plain: Boolean, //是否朴素按钮
autofocus: Boolean, //是否默认聚焦
round: Boolean,//是否圆角按钮
circle: Boolean //是否圆形按钮
},
computed: {
//父组件中的变量
_elFormItemSize() {
return (this.elFormItem || {}).elFormItemSize;
},
//按钮尺寸计算
buttonSize() {
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
},
//按钮是否禁用
buttonDisabled() {
return this.disabled || (this.elForm || {}).disabled;
}
},
methods: {
handleClick(evt) {
this.$emit('click', evt);
}
}
};
</script>
Radio组件
radio.vue
<template>
<label
class="el-radio"
:class="[
border && radioSize ? 'el-radio--' + radioSize : '',
{ 'is-disabled': isDisabled },
{ 'is-focus': focus },
{ 'is-bordered': border },
{ 'is-checked': model === label }
]"
role="radio"
:aria-checked="model === label"
:aria-disabled="isDisabled"
:tabindex="tabIndex"
@keydown.space.stop.prevent="model = isDisabled ? model : label"
>
<span class="el-radio__input"
:class="{
'is-disabled': isDisabled,
'is-checked': model === label
}"
>
<span class="el-radio__inner"></span>
<input
class="el-radio__original"
:value="label"
type="radio"
aria-hidden="true"
v-model="model"
@focus="focus = true"
@blur="focus = false"
@change="handleChange"
:name="name"
:disabled="isDisabled"
tabindex="-1"
>
</span>
<span class="el-radio__label" @keydown.stop>
<slot></slot>
<template v-if="!$slots.default">{{label}}</template>
</span>
</label>
</template>
<script>
import Emitter from 'element-ui/src/mixins/emitter';
export default {
name: 'ElRadio',
mixins: [Emitter],
inject: {
elForm: {
default: ''
},
elFormItem: {
default: ''
}
},
componentName: 'ElRadio',
props: {
value: {},
label: {},
disabled: Boolean,
name: String,
border: Boolean,
size: String
},
data() {
return {
focus: false
};
},
computed: {
isGroup() {
let parent = this.$parent;
while (parent) {
if (parent.$options.componentName !== 'ElRadioGroup') {
parent = parent.$parent;
} else {
this._radioGroup = parent;
return true;
}
}
return false;
},
model: {
get() {
return this.isGroup ? this._radioGroup.value : this.value;
},
set(val) {
if (this.isGroup) {
this.dispatch('ElRadioGroup', 'input', [val]);
} else {
this.$emit('input', val);
}
}
},
_elFormItemSize() {
return (this.elFormItem || {}).elFormItemSize;
},
radioSize() {
const temRadioSize = this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
return this.isGroup
? this._radioGroup.radioGroupSize || temRadioSize
: temRadioSize;
},
isDisabled() {
return this.isGroup
? this._radioGroup.disabled || this.disabled || (this.elForm || {}).disabled
: this.disabled || (this.elForm || {}).disabled;
},
tabIndex() {
return (this.isDisabled || (this.isGroup && this.model !== this.label)) ? -1 : 0;
}
},
methods: {
handleChange() {
this.$nextTick(() => {
this.$emit('change', this.model);
this.isGroup && this.dispatch('ElRadioGroup', 'handleChange', this.model);
});
}
}
};
</script>
解析:
(1)组件的html结构
<label ...>
<span class='el-radio__input'>
<span class='el-radio__inner'></span>
<input type='radio' .../>
</span>
<span class='el-radio__label'>
<slot></slot>
<template v-if="!$slots.default">{{label}}</template>
</span>
</label>
整个组件是一个外层label嵌套两个span。label放在最外面的作用是扩大鼠标点击范围,点击文字或者input都能触发响应。
第一个 表示圆形按钮,里面的span就是模拟的圆圈,input才是真正的radio按钮,通过样式隐藏了。
第二个表示文字部分。 默认渲染间的文本; {{label}}表示如果没有文本,但是标签,最后label的值会渲染出来。
这篇博文中有详细的说明:https://juejin.im/post/5b7a9d61e51d4538e5679462 强烈推荐!
(2)label部分
<label
class="el-radio"
:class="[
border && radioSize ? 'el-radio--' + radioSize : '',
{ 'is-disabled': isDisabled },
{ 'is-focus': focus },
{ 'is-bordered': border },
{ 'is-checked': model === label }
]"
role="radio"
:aria-checked="model === label"
:aria-disabled="isDisabled"
:tabindex="tabIndex"
@keydown.space.stop.prevent="model = isDisabled ? model : label"
>
1、 border && radioSize ? 'el-radio--' + radioSize : '',
radioSize 是Radio 的尺寸,仅在 border 为真时有效。radioSize是计算属性
radioSize() {
// props 的 size 及 form 注入的 size 及全局配置对象 ($ELEMENT, 此对象由引入时 Vue.use() 传入的默认空对象)的 size
const temRadioSize = this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
// 通过isGroup判断是否被 radio-group 组件包裹,如果包裹优先 radioGroupSize
return this.isGroup
? this._radioGroup.radioGroupSize || temRadioSize
: temRadioSize;
}
isGroup:获取当前组件的父级组件,然后检查其组件名是否是ElRadioGroup即单选框组,如果不是就继续检查父级的父级。这个方法会找到距离自己最近的父级ElRadioGroup组件,有则true,无则false
isGroup() {
let parent = this.$parent;
while (parent) {
if (parent.$options.componentName !== 'ElRadioGroup') {
parent = parent.$parent;
} else {
this._radioGroup = parent;
return true;
}
}
return false;
}
2、{ 'is-disabled': isDisabled } 表示是否禁用radio组件,与尺寸类似,就不说了。
3、 { 'is-checked': model === label } 表示当前按钮是否被选中,通过 model === label 控制。这里看下model:
model: {
get() { //取值
//通过isGroup判断是否被 radio-group 组件包裹,如果是就返回 radio-group的value:如果不是就返回自己的value
return this.isGroup ? this._radioGroup.value : this.value;
},
set(val) { //赋值
if (this.isGroup) {
// 被 radio-group 组件包裹 ,radio-group 组件发布 input 事件,传递值
this.dispatch('ElRadioGroup', 'input', [val]);
} else {
// 没有被 radio-group 组件包裹, 触发父组件上的input事件传递出val
this.$emit('input', val);
}
}
}
4、WAI-ARIA无障碍网页应用属性
role="radio"
:aria-checked="model === label"
:aria-disabled="isDisabled"
WAI-ARIA指无障碍网页应用。主要针对的是视觉缺陷,失聪,行动不便的残疾人以及假装残疾的测试人员。尤其像盲人,眼睛看不到,其浏览网页则需要借助辅助设备,如屏幕阅读器,屏幕阅读机可以大声朗读或者输出盲文。而ARIA就是可以让屏幕阅读器准确识别网页中的内容,变化,状态的技术规范,可以让盲人这类用户也能无障碍阅读!
5、:tabindex="tabIndex" :控制tab是否可以选中
tabIndex() {
//禁用状态或者单选按钮是在单选框组组件内且不是选中状态时,返回 -1;
return (this.isDisabled || (this.isGroup && this.model !== this.label)) ? -1 : 0;
}
当tabIndex=0时,该元素可以用tab键获取焦点,且访问顺序是按照元素在文档中的顺序来获取焦点;当tabIndex=1时,该元素用tab键获取不到焦点;当 tabindex > 0 的元素都切换之后,才会切换到 tabindex = 0 的元素,并且按出现的先后次序进行切换,这里的逻辑就是tab只能访问到选中状态下的单选按钮
6、@keydown.space.stop.prevent="model = isDisabled ? model : label"
空格键按下的时候给model赋值,但是不知道有什么用,不知道在什么场景下用得到。
(3)混入选项
import Emitter from 'element-ui/src/mixins/emitter';
mixins: [Emitter],
这里主要是用到了dispatch方法
dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
if (parent) {
//必须用apply定$emit的调用目标对象,因为是在父组件上触发该事件而不是在dispatch里
parent.$emit.apply(parent, [eventName].concat(params));
}
},
第一个参数是父级组件的名称,第二个是事件名称,第三个参数是一个数组或者单独的值,逻辑也很简单:不断地取到自己的父组件,判断是否是目标组件,如果不是继续去其父组件判断,如果是则在父组件上调用$emit触发事件。
handleChange() {
this.$nextTick(() => {
this.$emit('change', this.model);
//找到父组件去触发父组件的handleChange事件
this.isGroup && this.dispatch('ElRadioGroup', 'handleChange', this.model);
});
}
this.$on('handleChange', value => {
//在radio-group去触发父组件的change事件
this.$emit('change', value);
});
参考博文:
1、无障碍阅读:https://www.zhangxinxu.com/wordpress/2012/03/wai-aria-无障碍阅读/
2、Element源码分析系列4-Radio(单选框):https://juejin.im/post/5b7a9d61e51d4538e5679462
3、拜读及分析 Element 源码 - radio 组件篇:http://www.h3399.cn/201809/614690.html
element-ui button组件 radio组件源码分析整理笔记(一)的更多相关文章
- element-ui 组件源码分析整理笔记目录
element-ui button组件 radio组件源码分析整理笔记(一) element-ui switch组件源码分析整理笔记(二) element-ui inputNumber.Card .B ...
- element-ui Carousel 走马灯源码分析整理笔记(十一)
Carousel 走马灯源码分析整理笔记,这篇写的不详细,后面有空补充 main.vue <template> <!--走马灯的最外层包裹div--> <div clas ...
- element-ui inputNumber、Card 、Breadcrumb组件源码分析整理笔记(三)
inputNumber组件 <template> <!--@dragstart.prevent禁止input中数字的拖动--> <div @dragstart.preve ...
- element-ui input组件源码分析整理笔记(六)
input 输入框组件 源码: <template> <div :class="[ type === 'textarea' ? 'el-textarea' : 'el-in ...
- element-ui Pagination组件源码分析整理笔记(七)
element-ui源码的版本是2.4.9 pagination.js import Pager from './pager.vue'; import ElSelect from 'element-u ...
- element-ui Upload 上传组件源码分析整理笔记(十四)
简单写了部分注释,upload-dragger.vue(拖拽上传时显示此组件).upload-list.vue(已上传文件列表)源码暂未添加多少注释,等有空再补充,先记下来... index.vue ...
- element-ui MessageBox组件源码分析整理笔记(十二)
MessageBox组件源码,有添加部分注释 main.vue <template> <transition name="msgbox-fade"> < ...
- element-ui switch组件源码分析整理笔记(二)
源码如下: <template> <div class="el-switch" :class="{ 'is-disabled': switchDisab ...
- element-ui Message组件源码分析整理笔记(八)
Message组件源码: main.js import Vue from 'vue'; import Main from './main.vue'; import { PopupManager } f ...
随机推荐
- 【ElasticSearch】:Mapping相关
Mapping 类似数据库中的表结构定义,主要作用如下: 定义Index下的字段名(Field Name). 定义字段类型,例如数值型.字符串型.布尔型等. 定义倒排索引相关配置,比如是否索引.记录p ...
- isBalanced函数实现
原文:从一道面试题谈起,作者:360奇舞团 刘观宇 题目: 创建一个函数来判断给定的表达式中的大括号是否闭合,返回 true/false,对于空字符串,返回 true var expression = ...
- Shell - 简明Shell入门07 - 数组(Array)
示例脚本及注释 #!/bin/bash test0=() # 定义数组 test1=(a b c d e f) # 定义数组 test2=( # 定义数组 'A?' "BB!" C ...
- iOS 数据持久化-- FMDB
一.简介 1.什么是FMDB FMDB是iOS平台的SQLite数据库框架 FMDB以OC的方式封装了SQLite的C语言API 2.FMDB的优点 使用起来更加面向对象,省去了很多麻烦.冗余的C语言 ...
- Mysql数据引擎和系统库
系统数据库 information_schema: 虚拟库,不占用磁盘空间,存储的是数据库启动后的一些参数,如用户表信息.列信息.权限信息.字符信息等performance_schema: MySQL ...
- Windows+MyEclipse+MySQL【连接数据库报错caching_sha2_password】
在MyEclipse中打开[窗口]->[显示视图]-> MyEclipse Database Browser -> (图片里倒三角)New.... ①Driver template: ...
- [Leetcode]303.区域和检索&&304.二维区域和检索
题目 1.区域和检索: 简单题,前缀和方法 乍一看就觉得应该用前缀和来做,一个数组多次查询. 实现方法: 新建一个private数组prefix_sum[i],用来存储nums前i个数组的和, 需要找 ...
- ubuntu图形化配置
安装图形界面 apt-get install ubuntu-desktop 配置用户目录 udo useradd -s /bin/bash -d /home/wykeinstein -m wykein ...
- Toast优化,解决频繁点击多次出现
日常用到Taost的机会很多,用就大家都会用,但是直接使用时,频繁点击Toast就会频繁出现,点击多少次就出现多少次,如果你不在页面的生命周期相应位置cancel掉Toast的话,即使退出了页面也是会 ...
- Attr的visitNewClass()方法解读
在visitNewClass()方法中有如下注释: We are seeing an anonymous class instance creation.In this case, the class ...