element-ui 源码解析 一
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按钮,通过样式隐藏了。
第二个表示文字部分。 默认渲染间的文本;
(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);
});
element-ui 源码解析 一的更多相关文章
- element ui源码解析 -- input篇
el-input是element ui中使用最频繁的组件之一了,分析其构成从四个方面入手:DOM结构,属性,样式,事件入手 DOM结构: <div> <input /> < ...
- element ui源码解析 -- button篇
要看源码就得从最简单的开始,button够简单的了,就从他开始吧. 安装依赖后源码目录在:node_modules/element-ui/packages中,可以看到这里的文件夹命名是不是很熟悉,就是 ...
- Element UI 源码—— Carousel 走马灯学习
参考博客:https://segmentfault.com/a/1190000014384638?utm_source=tag-newest
- vue UI库iview源码解析(2)
上篇问题 在上篇<iview源码解析(1)>中的index.js 入口文件的源码中有一段代码有点疑惑: /** * 在浏览器环境下默认加载组件 */ // auto install if ...
- 【vuejs深入二】vue源码解析之一,基础源码结构和htmlParse解析器
写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. vuejs是一个优秀的前端mvvm框架,它的易用性和渐进式的理念可以使每一个前端开发人员感到舒服,感到easy.它内 ...
- 【原创】backbone1.1.0源码解析之View
作为MVC框架,M(odel) V(iew) C(ontroler)之间的联系是必不可少的,今天要说的就是View(视图) 通常我们在写逻辑代码也好或者是在ui组件也好,都需要跟dom打交道,我们 ...
- 【转载】Xutils3源码解析
Github源码地址:https://github.com/wyouflf/xUtils3 原文地址 :http://www.codekk.com/blogs/detail/54cfab086c476 ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- jQuery2.x源码解析(缓存篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...
- EventBus3.0源码解析
本文主要介绍EventBus3.0的源码 EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递. EventBus使用简单,并将事件发布和订阅充 ...
随机推荐
- [TJOI2017]城市
嘟嘟嘟 这题刚开始想复杂了,想什么dp去了,其实没那么难. 考虑断掉一条边,记分离出来的两棵子树为A和B,那么合并后的树的直径可能有三种情况: 1.A的直径. 2.B的直径 3.A的半径+边权+B的半 ...
- 转://Linux下误删除/home目录的恢复方法
一般情况下,我们在安装Oracle数据库的时候,都会创建一个Oracle用户,用该用户来安装和管理Oracle.Oracle用户的根目录就是/home/oracle. 通常安装Oracle数据库是按照 ...
- 转://如何增加linux根目录的磁盘空间(基于LVM)?
问题引出: 在测试过程中替换so文件,报磁盘空间不足的错误. ▲问题分析: 由于当时系统部署架构的考虑,把软件和数据库部署在了同一台机器上,并且给了30G的磁盘空间.系统上占用磁盘空间的有2部分,一是 ...
- ORACLE直方图(10g)
为什么需要直方图 ?当表中一列数据比较的值分布比较均匀时,optimzer可以很好的通过最大值,最小值和NDV(唯一值的个数),就可以判断出cardinality.对于cardinality越精确,o ...
- pycharm企业版注册码
pycharm下载最新版 链接:https://pan.baidu.com/s/1gKOCf3PQFc1_2amkMUU1-A 提取码:9pt0 下载企业版: http://www.jetbrains ...
- 三、Oracle 查询+where条件
一.查询1.distinct:查询去除重复的行,是所有的列都重复才满足条件2.列别名:as或者空格 select name as 姓名 from student3.查询字段可以做数学运算,也可以做字符 ...
- Quartz动态添加定时任务执行sql(服务启动添加+手动添加)
系统用来每天插入视图数据... 一.数据库表设计 1.接口配置表(t_m_db_interface_config) 2.接口日志表(t_m_db_interface_log) 3.前端配置页面 查询页 ...
- TFT2.0液晶屏幕测试
型号 尺寸:2.0寸 驱动芯片: ILI9225 分辨率:176*220 SPI 板载3.3v电平转换,直接插在arduino上可用. 安装这个库 这个库默认是 uno 板子的,如果换成mega256 ...
- Flink 集群运行原理兼部署及Yarn运行模式深入剖析
1 Flink的前世今生(生态很重要) 原文:https://blog.csdn.net/shenshouniu/article/details/84439459 很多人可能都是在 2015 年才听到 ...
- Netty入门(二)之PC聊天室
参看Netty入门(一):Netty入门(一)之webSocket聊天室 Netty4.X下载地址:http://netty.io/downloads.html 一:服务端 1.SimpleChatS ...