本文的研究思路是通过阅读Element源码,然后自动动手一步一步编写组件,完善其对应功能。
Layout布局
研究过Input和Button之后,再来看看Element布局的实现。看看有什么值得我们学习的。
Element是通过24 分栏,迅速简便地创建布局。实现原理是利用百分比,将一行视为宽度100%,等分为24份。
- 如果你的列占:span=24。那么样式为.el-col-24 { width: 100%; }。
- 如果你的列占:span=12。那么样式为.el-col-12 { width: 50%; }。
- 如果你的列占:span=8。那么样式为.el-col-8 { width: 33.3333%; }。依次类推。
原理就是根据span属性控制col组件样式。
详细代码在码云:https://gitee.com/DaBuChen/my-element-ui/tree/layout/
Link 文字链接组件
相比于Button组件,Link组件的逻辑就简单的多了。区别于原生a标签,增加了type, disable属性,还支持下划线的控制,美化了样式。
这里可以稍微总结下Element封装组件的思路。
一是扩展了原生标签的功能。使一些常用的功能通过属性可以直接支持。比如Link的禁用。配置个disable属性就可以达到。若我们用原生标签a来写的话,需要自己写些逻辑。
二是提供了统一的视觉风格。使我们靠堆积组件就能有不错的视觉效果。如Button、Link都有固定几种type类型。展示主要、成功、信息、警告、危险等反馈给用户。
详细代码在码云:https://gitee.com/DaBuChen/my-element-ui/tree/link/
Radio 单选框
继续我们的研究之旅,看看Radio扩展原生了哪些功能。
原生radio需要有相同的name,才能达到单选功能。
<form>
你更喜欢哪种颜色?<br>
<input type="radio" name="colors" checked id="red">红色<br>
<input type="radio" name="colors" id="blue">蓝色<br>
<input type="radio" name="colors" id="green">绿色
</form>
原生radio通过checked属性来标识选没选中。Element的radio的方式完全不一样,让多个radio的v-model绑定同一个属性,当属性的值与label相同时,该项就选中了,其他项因为v-model的值跟lablel不同,就不被选中。
<el-radio v-model="radio" label="1">备选项</el-radio>
<el-radio v-model="radio" label="2">备选项</el-radio>
最基本的实现
实现最基本的功能,有新意的地方是computed的model属性,使用get/set来存取值,近而控制原生radio的checked选中不选中:
<template>
<label class="el-radio"
:class="[
{ 'is-checked': model === label }
]"
>
<span class="el-radio__input"
:class="{
'is-checked': model === label
}"
>
<span class="el-radio__inner"></span>
<input
ref="radio"
class="el-radio__original"
:value="label"
type="radio"
v-model="model"
@change="handleChange"
>
</span>
<span class="el-radio__label" @keydown.stop>
<slot></slot>
<template v-if="!$slots.default">{{label}}</template>
</span>
</label>
</template>
<script>
export default {
name: 'ElRadio',
props: {
value: {},
label: {},
},
computed: {
model: {
get() {
return this.value;
},
set(val) {
this.$emit('input', val);
this.$refs.radio && (this.$refs.radio.checked = this.model === this.label);
}
},
},
methods: {
handleChange() {
this.$nextTick(() => {
this.$emit('change', this.model);
});
}
}
}
</script>
效果如下:
禁用状态
为外层标签添加禁用样式{ ‘is-disabled’: disabled },为input[‘radio’]标签绑定:disabled="disabled"即可。
效果如下:
单选框组
为每个el-radio绑定v-model略显麻烦,所以增加了一个el-radio-group。将v-model绑在其上。这将会是个有意思的实现。
<template>
<el-radio-group v-model="radio">
<el-radio :label="3">备选项</el-radio>
<el-radio :label="6">备选项</el-radio>
<el-radio :label="9">备选项</el-radio>
</el-radio-group>
</template>
<script>
export default {
data () {
return {
radio: 3
};
}
}
</script>
先来实现一个基本的el-radio-group:
<template>
<div
class="el-radio-group"
role="radiogroup"
>
<slot></slot>
</div>
</template>
<script>
export default {
name: 'ElRadioGroup',
componentName: 'ElRadioGroup',
props: {
value: {},
}
}
</script>
非常简单,用value来接收v-model的值。用componentName标识自己,方便el-radio寻找即可。重头戏还在el-radio的实现上。
首先实现一个广播器,就是可以让事件一层层的冒泡上去,具体代码在这里不贴,可以看我后续传到码云上的源码。然后我们用mixin引入它。
import Emitter from '../../mixins/emitter'
...
mixins: [Emitter],
更改model的get/set:
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);
}
this.$refs.radio && (this.$refs.radio.checked = this.model === this.label);
}
},
...
isGroup() {
let parent = this.$parent;
while (parent) {
if (parent.$options.componentName !== 'ElRadioGroup') {
parent = parent.$parent;
} else {
this._radioGroup = parent;
return true;
}
}
return false;
},
通过isGroup判断当前el-radio是否在el-radio-group中。如果在,model的get就取el-radio-group的value,set就利用广播器的dispatch方法触发el-radio-group的input事件,达到改变用户在el-radio-group的v-model绑定属性的值的目的。
带有边框
这个就是样式的添加。根标签class添加{ ‘is-bordered’: border }即可。 效果如下:
按钮样式
按钮样式的radio,Element写了一个新组件radio-button。基本逻辑与radio组件类似。下面贴出代码:
<template>
<label class="el-radio-button"
:class="[
{ 'is-active': value === label },
{ 'is-disabled': disabled },
{ 'is-focus': focus }
]"
>
<input
class="el-radio-button__orig-radio"
:value="label"
type="radio"
v-model="value"
@change="handleChange"
:disabled="disabled"
>
<span
class="el-radio-button__inner"
@keydown.stop>
<slot></slot>
<template v-if="!$slots.default">{{label}}</template>
</span>
</label>
</template>
<script>
import Emitter from '../../mixins/emitter'
export default {
name: 'ElRadioButton',
mixins: [Emitter],
props: {
label: {},
disabled: Boolean,
},
computed: {
value: {
get() {
return this._radioGroup.value;
},
set(value) {
this._radioGroup.$emit('input', value);
}
},
_radioGroup() {
let parent = this.$parent;
while (parent) {
if (parent.$options.componentName !== 'ElRadioGroup') {
parent = parent.$parent;
} else {
return parent;
}
}
return false;
},
},
methods: {
handleChange() {
this.$nextTick(() => {
this.$emit('change', this.model);
});
},
}
}
</script>
效果如下:
详细代码在码云:https://gitee.com/DaBuChen/my-element-ui/tree/radio/
其他组件源码研究:
Element组件源码研究-Layout,Link,Radio
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 猎户座小陈 原文链接:https://juejin.im/post/6854573219660496909