vue组件之Tabs标签页

预览地址 这篇博客意在总结记录Bug的解决和完成组件的过程。css

Tabs标签页

<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控制显示。 数组

大佬的方法仍是牛批,但这里我不想多写代码就直接借鉴(chao xi)之前的 轮播

只需在移出的时候绝对定位一下就好了promise

position: absolute;
 left: 0;
 top: 0;
复制代码

而后无非就是一个从x(左/右)移入,一个反向移出就好了。浏览器

line的位移

line的位移应该是这部分相对比较麻烦的。之因此选择用div单独一个元素来作高亮线条,而不是header-itemborder。是由于

  • 更容易变换位置,适合后面的position变换
  • 能够调整高度宽度

大概的css

首先父元素确定要相对定位

.tabs-header{
            display: flex;
            //....
            position: relative;
            height: $tab-height;
            //....
 }
复制代码

子元素绝对定位

>.line{
        position: absolute;
        bottom: 0;
        left: 0;
       //....
        }
复制代码

根据不一样的positionline完成不一样方向和位置的偏移

这样子会写至关多的重复代码,很是不利于阅读和维护,例如。

代码重构:表驱动编程

先作命名上的修改

const {line} = this.$refs
        let [left,top] = [item.offsetLeft,item.offsetTop]
        let {width,height} = item.getBoundingClientRect()
        let positionName
复制代码

这里面看到其实每一个位置的设置都只须要三个属性,并且topbottomleftright都是同样的设置。 因此

//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()
               //......
            })
           //.....
        }
复制代码

后面会继续仿着elementAntd的组件效果,实现关于标签页更多功能。 0

博客里面若是有不正确或者错误的地方,但愿大佬们能够批评指正。 若是你以为将就还行,给个人轮子项目一个star就是最大的鼓励了。

相关文章
相关标签/搜索