虽然是源码分析,但终究还是需要自己敲一遍。很多时候,只是以为自己会了,其实只是看的懂了,但是不看源代码的话,估计还是两眼一黑。
所以,这次打算写的更详细一点。从一些简单的功能开始,一步步的去分析源码,通过给组件迭代分析,一个版本一个版本地迭代,逐步实现不同的功能。
Version-0.1-实现了显示整数评分,高分和低分之间的图标、颜色变化
最初始的版本,暂时先不考虑半星这种情况。
<template>
<div class="me-rate">
<span
v-for="item in max"
class="me-rate-item">
<i
:class="[getIconClass(item-1)]"
:style="getIconColor(item)"
class="el-rate-icon"></i>
</span>
<span
v-if="showText"
class="me-rate-text"
:style="{color: textColor}">{{ text }}</span>
</div>
</template>
props: max -> 评分的最高分
然后根据max值, 循环遍历max个图标节点。而对于高分和低分之间的变化就在于这两句:
:class="[getIconClass(item-1)]"
:style="getIconColor(item)"
给每个图标绑定不同的class 和 style。
举个例子,对于图标,如果评分为3,总分为5.
- 则第1-3个图标应该为activeClass, 4-5个图标为voidClass
- activeClass 和 voidClass的值从哪里取呢? ——> classMap。当组件初始化的时候,通过props的colors 和 iconClass的值,初始化这两个map集合。
- 很巧妙的一点在于,对于图标和颜色的取值,都是通过getValueFromMap这个函数实现。
- 不同等级获取不同的值,就需要lowThreshold, highThreshold这两个props来进行判断临界值的判断。
分析到这里,再看version-0.1版本的代码,应该问题就不大了。
version-0.2-半星的实现与disabled
在前面,如果为voidClass, 我们直接获取的是 this.classMap.voidClass, 但是disabled的时候,voidClass的样式不同,依旧为实心的,所以要添加 disabledVoidClass, 在props, computed, classMap三处都要进行修改。
对于半星的实现,在原有的基础上,给图标增加子元素, 绝对定位,设置宽度为50%(如果小数部分大于0.5)。这个子元素(半星)的图标和颜色样式肯定为 activeClass、activeColor.
<i
:class="[getIconClass(item)]"
:style="getIconColor(item)"
class="el-rate-icon">
<i
v-if="showDecimalIcon(item)"
:class="decimalIconClass"
:style="decimalStyle"
class="me-rate-decimal"></i>
</i>
version-0.3-实现hover、click动态选择分数
这一部分的重点在于理解v-model的本质
<input v-on:input v-bind:value />
所以可以试一下,如果将props中的value换一个名字,那么,在调用组件时v-model并不能正确的绑定值。
相应的,在点击的时候,触发selectValue函数,通过this.$emit('input', **)手动地来触发v-model的改变。
selectValue() {
if(this.disabled) {
return;
}
this.$emit('input', this.currentValue);
},
而对于hover的时候,改变图标的选中状态,这段代码很有意思
setCurrentValue(value, event) {
if(this.disabled) {
return;
}
if(this.allowHalf) {
let target = event.target;
if(hasClass(target, 'me-rate-item')) {
target = target.querySelector('.el-rate-icon');
}
if(hasClass(target, 'me-rate-decimal')) {
target = target.parentNode;
}
this.pointerAtLeftHalf = event.offsetX * 2 <= target.clientWidth;
this.currentValue = this.pointerAtLeftHalf ? value -0.5 : value;
}else {
this.currentValue = value;
}
this.hoverIndex = value;
},
首先,不管hover到父元素还是半星子元素上,都先查找到el-rate-icon元素。然后通过offsetX 和 clientWidth 来判断hover的位置是否过半。相应的改变 currentValue.