预览地址 这篇博客意在总结记录Bug的解决和完成组件的过程。css
<x-tabs :selected.sync="selected">
<x-tabs-item item="经济" name="1">
财经报道
</x-tabs-item>
</x-tabs>
复制代码
按照上面的结构,内容应该放在tabs-item
,那么选择既
就由tabs
控制。vue
tabs
大体HTML结构react
<div class="tabs">
<div class="tabs-header"........>
<divclass="tabs-header-item".........>
// 标签页,这里简称 header-item
//.........
</div>
<div ref="line" class="line" v-if="!cards"></div>
//高亮线条
</div>
<div class="tabs-content">
<slot></slot>
//展现内容
</div>
</div>
复制代码
因为只有两个组件,因此组件通讯就很是简单了。这里不作多的说明git
如同预览所见的(目前全部position只有一种动画效果),有一种轮播切换的赶脚。github
通常这种动画容易形成一些bug,例如编程
动态组件
,key
或者mode='xxx'
什么的。可是slot
这种至关麻烦,在不知道插入元素的状况下,很难作到动画的顺畅和没有额外的bug。钩子函数
都设置为绝对定位,结束改回来。但动画期间父元素失去高度。会有抖动的不美观效果。首先我去Ant Design看了看。 由于这个动画效果原本就是模仿他的。api
大概猜了一下,Ant Design的作法应该是把三组并排,经过控制父级的负margin
控制显示。 数组
只需在移出的时候绝对定位一下就好了promise
position: absolute;
left: 0;
top: 0;
复制代码
而后无非就是一个从x(左/右)移入,一个反向移出就好了。浏览器
line
的位移应该是这部分相对比较麻烦的。之因此选择用div
单独一个元素来作高亮线条,而不是header-item
的border
。是由于
position
变换大概的css
首先父元素确定要相对定位
.tabs-header{
display: flex;
//....
position: relative;
height: $tab-height;
//....
}
复制代码
子元素绝对定位
>.line{
position: absolute;
bottom: 0;
left: 0;
//....
}
复制代码
position
,line
完成不一样方向和位置的偏移这样子会写至关多的重复代码,很是不利于阅读和维护,例如。
代码重构:表驱动编程
先作命名上的修改
const {line} = this.$refs
let [left,top] = [item.offsetLeft,item.offsetTop]
let {width,height} = item.getBoundingClientRect()
let positionName
复制代码
这里面看到其实每一个位置的设置都只须要三个属性
,并且top
和bottom
,left
和right
都是同样的设置。 因此
//linemove函数,这名字真的不行,后面改改。
let position = {
topOrBottom:{
width:`${width}px`,
height:0,
transform: `translate(${left}px,0)`
},
leftOrRight:{
width: 0,
height:`${height}px`,
transform: `translateY(${top}px)`
}
}
positionName = this.position === 'left' || this.position === 'right' ? 'leftOrRight' : 'topOrBottom';
line.style.height = position[positionName].height
line.style.width = position[positionName].width
line.style.transform = position[positionName].transform
复制代码
这样子的修改就使代码可读性更强,一目了然。
line
位置不正确的问题其实原本是对的,展现组件的时候,position
绑定动态数据,在button
切换的时候,发现line
位置不正确。
watch:{
position(){
this.lineMove()
}
},
复制代码
这里简单讲一下我对$nextTick
的理解:
return function queueNextTick (cb?: Function, ctx?: Object){
let _resolve
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
//.......
}
} else if (_resolve) {
//... 这里是传入的回调为空且支持【promise】的时候默认选择的【promise】,这在官网里面有讲过
}
})
}
复制代码
先对传入的回调函数
作一个收集,放进一个callbacks
数组里面
事实上$nextTick
也但愿异步任务能够尽快一点执行。因此在api
的选择上是
‘微任务’
的promise
,看看浏览器是否原生支持promise
macroTimerFunc
macroTimerFunc
里根据浏览器的支持程度对这些异步api
作一个选择(setImmediate
,messagechannel
....),反正setTimeout
是最低的。而后会在下一个tick
执行(flushCallbacks
)对callbacks
作个从头开始的遍历执行(就像是队列同样),这里具体的比较细。反正最终就是依次执行传入的nextTick
就对了。
这样子看来,在我测试这里的状况里应该是选择的promise
,这固然是不行的。
由于动画过渡的影响,在执行的时候,动画也在执行,这期间元素的css属性并非我想要的。
如今的选择就是 移除展现的动画过渡效果,其实就是展现组件,位置改变的动画没了,其余正常使用的过渡效果依然存在。
最后的代码
mounted(){
this.$nextTick(()=>{
this.lineMove()
//......
})
//.....
}
复制代码
后面会继续仿着element和Antd的组件效果,实现关于标签页更多功能。 0
博客里面若是有不正确或者错误的地方,但愿大佬们能够批评指正。 若是你以为将就还行,给个人轮子项目一个star就是最大的鼓励了。