序言
本章就来分享一下Element-ui 源码的Radio篇,文章的节奏是按照正常的Element目录结构来分享,首先就是Form里面的组件。这样的话第一个就是Radio了
组件流程
- 组件代码从 0 到 1 的过程
- 对应的组件里面index引入install方法并抛出
- main.js 集体引入
- 使用Vue.use方法注册
- 最后页面中使用
最后呢 就是源码的位置了:packages/radio/src/radio.vue。
radio文件夹下有一个index.js用于注册组件然后抛出,src是用来放组件代码的,但是这radio的组件代码相比于上次的Button就比较多了很多,radio.vue 、 radio-button.vue 、radio-group.vue,如果经常用Element的童鞋们大致的看到这些文件的名称应该也能猜到个七七八八了。
结构分析
一、分析的话 我们从每个vue文件的三个方面着手:DOM结构、数据属性、事件、
二、Element针对于radio做了三大类的工作分别是 基础用法、单选框组、按钮样式 根据上面的vue文件可以一一对应.
radio基础用法
DOM结构
<label>
<span>
<span></span>
<input/>
</span>
<span>
<slot></slot>
<template v-if="!$slots.default">{{ label }}</template>
</span>
</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"
>
<span
class="el-radio__input"
:class="{
'is-disabled': isDisabled,
'is-checked': model === label,
}"
>
<span class="el-radio__inner"></span>
<input
ref="radio"
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>
看到这后会有一个疑问? 为什么Radio不单单的是一个input标签,为什么需要label来包裹一下, label标签顾名思义是展示类的标签,但是他还是有一个另外的作用!
在单独的input框中,用户想要输入内容时只能点击输入框才可以使输入框获取焦点。
< inpust id="account" name="account" >
如果在input的周围加一个label,那么点击label显示的文字时,焦点也会自动集中在输入框中。具体的使用方法是label的for=“输入框的id”
< label for="account">账号< /label > < input id="account" name="account"/ >
在点击label的时候触发了两次操作,一次是label的点击,一次是input自动获取焦点。
label
-
class="el-radio" : 单选样式
-
class : 针对于样式的
--border && radioSize ? 'el-radio--' + radioSize : '' : radio大小 Radio 的尺寸,仅在 border 为真时有效
--is-disabled': isDisabled : 禁用
--is-focus': focus : 选中的样式
--is-bordered': border : 边框
--is-checked': model === label : 按钮是否被选中
-
role="radio" : 无障碍网页应用属性
--:aria-checked="model === label"
--:aria-disabled="isDisabled"
无障碍网页应用。主要针对的是视觉缺陷,失聪,行动不便的残疾人以及假装残疾的测试人员。 尤其像盲人,眼睛看不到,其浏览网页则需要借助辅助设备,如屏幕阅读器,屏幕阅读机可以大声朗读或者输出盲文。 而ARIA就是可以让屏幕阅读器准确识别网页中的内容,变化,状态的技术规范,可以让盲人这类用户也能无障碍阅读!
-
:tabindex="tabIndex" : tab键获取焦点 -1 代表不可选, 0 代表可选
-
@keydown.space.stop.prevent="model = isDisabled ? model : label" :
keydown.space 按下空格、
stop 停止冒泡、
prevent 阻止默认行为
当tab选中当前 radio 在键盘上敲击空格键的时候(space即空格键 ),阻止了原生事件发生
label > span
-
class="el-radio__input" : 单选样式
-
class : --is-disabled: isDisabled, 禁用 --is-checked': model === label :是否选中
label > input
-
ref="radio" : 注册ref
-
class="el-radio__original" : opacity: 0 对其隐藏!!
-
:value="label" : Radio的value
-
type="radio" : input类型
-
aria-hidden="true" : 1.让这个元素对浏览器隐藏 2.为了避免屏幕识读设备抓取非故意的和可能产生混淆的输出内容
-
v-model="model" : 绑定值
-
@change="handleChange" : 原生change事件
-
:name="name" : 多个相同name属性为一组
-
:disabled="isDisabled" : 禁用
-
tabindex="-1" : 禁止tab选中
注意:如果使用-1值时,onfocus与onblur事件仍被启动
知识点:为啥不用原生 input 而是自己模拟
1.原生标签 radio不同浏览器样式不同,所以自己写来代替
2.需要用到原生的 radio来获取焦点来触发 change事件,所以 element是 采用了 绝对定位老脱离文档流,
以及设置透明度为0,而不是采用 dispaly:none或者 visibility:hidden,因为这样就无法点击了
数据属性
name: "ElRadio",
mixins: [Emitter],
inject: { // 与 Form 表单相关 这里也不说了 暂且没有用到
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) {
// 向上循环找它爸爸,当能找到父元素组件名是ElRadioGroup的,则返回true,否则false
if (parent.$options.componentName !== "ElRadioGroup") {
parent = parent.$parent;
} else {
//若是radio group,保存group组件的引用
this._radioGroup = parent;
return true;
}
}
return false;
},
model: {
get() {
// 如果是 radio-group 则取 radio-group 上面的值,否则则取 radio 本身的值
return this.isGroup ? this._radioGroup.value : this.value;
},
set(val) {
if (this.isGroup) {
this.dispatch("ElRadioGroup", "input", [val]);
} else {
this.$emit("input", val);
}
this.$refs.radio && (this.$refs.radio.checked = this.model === this.label);
},
},
_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() {
//禁用状态或者单选按钮是在单选框组组件内且不是选中状态时,返回 -1;
return this.isDisabled || (this.isGroup && this.model !== this.label) ? -1 : 0;
}
}
computed
-
isGroup(): 判断是否存在radio group
-
model :
-
_elFormItemSize : 获取fromItem的大小
-
radioSize : 控制radio的大小
-
isDisabled : 该组件是否是禁用状态的
-
tabIndex : 用于设置组件的tab-index值
事件
handleChange() {
this.$nextTick(() => {
// 触发实例上面的change事件
this.$emit("change", this.model);
// group的情况下,触发名为ElRadioGroup的父组件下的handleChange事件,也将当前选中值作为参数
this.isGroup && this.dispatch("ElRadioGroup", "handleChange", this.model);
});
},
因为Radio比较多 所以分两篇来写,下一篇就是radio-group、radio-button。
招聘广告
言重式招聘 寻人!!!寻志同道合之人、寻竭忠尽智之人、寻深思远虑之人、寻勤恳至诚之人
浙江大华技术股份有限公司-软研-智慧城市产品研发部招聘高级前端!!!!!
欢迎大家来聊,有意向可发送简历到chen_zhen@dahuatech.com
芸芸众生,孰不爱生 ? 爱生之极,进而爱群 ---- 无奖竞猜