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就是最大的鼓勵了。

相關文章
相關標籤/搜索