Badge组件主要用于数字或状态的标记,对于消息类的提醒功能,使用这组件仍是很常见的。具体显示效果以下图:css
无论组件复杂仍是简单,编码实现这个组件的都不是源码分析目的。
源码分析,在于经过一步步的实现这个功能,在这个过程当中,看本身能学到什么,发现哪些问题。
而后经过这些问题,去扩展知识点,最终完成 实践 - 思考 - 扩展
这三步。html
demo-1连接vue
第一步的实现很简单,具体的能够看示例代码,主要点在于css的思路。api
仔细看,标记相对于父元素,badge的显示区域定位时只有左上角的一小部分和父元素重叠。经过下面的css能够实现这个功能。源码分析
<div class="al-badge"> <slot></slot> <span class="al-badge--content">{{value}}</span> </div>
.al-badge { display: inline-block; position: relative; } .al-badge--content { position: absolute; right: 10px; top: 0; transform: translateX(100%) translateY(-50%); }
开始的时候,我还想着怎么经过right + margin属性实现。可是看源码发现这种实现方式,感受也是很简洁。那么问题就来了,对于css的百分比单位,哪些属性能够设置呢?设置后他们的百分比又是相对于哪一个元素?post
发现一篇写的很好的总结,能够看一下:详述css中的百分比值
总结一下:this
widht & heiht
属性,百分比值相对于包含块的宽高
margin & padding
属性,百分比值相对于包含块的宽度
font-size
属性,百分比值相对于直接父元素的font-size
line-height
属性,百分比值相对于自身元素的font-size
vertical-align
属性,百分比值相对于自身元素的line-height
top/left/right/bottom
属性,百分比值相对于包含块宽高
translate
中,百分比值相对于自身元素border-box盒模型的宽高
demo-2连接编码
一开始,对于这个需求,个人实现思路是这样的:spa
<div class="al-badge"> <slot></slot> <span class="al-badge--content" v-if="value <= max">{{value}}</span> <span class="al-badge--content" v-if="value > max">{{max+}}</span> </div>
这算是本身在初期常常容易犯的一个问题吧。滥用v-if v-else。
一个好的组件,实现起来,模板一眼看过去,就应该是很简洁的。过多的v-if只会越读越糟心,后期维护以及增长新的需求时,也容易各类踩坑。.net
ok,咱们来从新梳理一下上面这段html,两个v-if的模板,只是针对于不一样条件下,内容显示不一样而已。因此,可不能够增长一个computed属性,在js这一层进行简化?看下面这一段简化后的:
let ElBadge = { name: 'ElBadge', //..... computed: { content: function() { if(typeof this.value === 'number' && typeof this.max === 'number') { return this.value > this.max ? `${this.max}+` : this.value; } return this.value; } } };
对于这个功能,首先须要解决的是,怎么判断组件是单独使用,仍是包裹了一个子元素?
看下Vue关于内容分发的文档:https://cn.vuejs.org/v2/api/#...
好,这样经过$slots.default
能够解决这个问题了,那么模板上怎么体现这个区别呢?v-if新增单独使用的状况?和上一点相似,咱们须要保证html模板的简洁,因此经过css类名来作区别,当单独使用的时候,就不想对于左上角进行定位了。
<div class="al-badge"> <slot></slot> <span v-show="!hidden" class="al-badge--content" :class="[ isDot && 'is-dot', $slots.default && 'is-fixed' ]"> {{content}} </span> </div>
.al-badge--content { //..... &.is-fixed { position: absolute; right: 10px; top: 0; transform: translateX(100%) translateY(-50%); } &.is-dot { right: 5px; width: 8px; height: 8px; padding: 0; } }
经过Badge组件
参考文献: