預覽地址 這篇博客意在總結記錄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就是最大的鼓勵了。