骨架屏用途
- 作為spa中路由切換的 loading, 結(jié)合組件的生命周期和ajax請求返回的時機來使用.( 作為loading 使用)。作為與用戶聯(lián)系最為密切的前端開發(fā)者,用戶體驗是最值得關(guān)注的問題。關(guān)于頁面loading狀態(tài)的展示,主流的主要有l(wèi)oading圖和進度條兩種。除此之外,越來越多的APP采用了“骨架屏”的方式去展示未加載內(nèi)容,給予了用戶煥然一新的體驗。
- 作為首屏渲染的優(yōu)化
Vue架構(gòu)骨架屏
思路大綱
- 定義一個抽象組件,在抽象組件的render函數(shù)里獲取插槽
- 深度循環(huán)遍歷插槽,將每個元素都添加上gm-skeleton的類名
- 將vnode textContent預(yù)暫后清空保證骨架屏出現(xiàn)時不會出現(xiàn)默認文字
- 返回slots
定義一個抽象組件
什么是抽象組件? 在渲染時會被跳過,只做運行時的操作的組件
1
2
3
4
|
export default { name: 'GmSkeleton' , abstract: true // 抽象組件的屬性 } |
獲取插槽并初始化操作骨架屏
1
2
3
4
5
6
7
8
9
10
|
render(h) { const slots = this .$slots. default || [h( '' )] this .$nextTick().then(() => { this .handlerPrefix(slots, this .showSpin ? this .addSkeletPrefix : this .removeSkeletPrefix) }) return slots.length > 1 ? h( 'div' , { staticClass: this .showSpin ? 'g-spinner' : '' }, slots) : slots } |
這里我們將處理slots的方法放置在nextTick里面, 因為handlerPrefix里需要獲取真實的DOM,nextTick是用來執(zhí)行排序后的更新隊列里的所有方法, 在執(zhí)行render前, GMSkeleton組件的renderWatcher已被收集到更新隊列里,所以此時定義nextTick CallBack函數(shù)里能獲取到渲染后對應(yīng)插槽里所有真實DOM,若是不了解nextTick原理,請移步你不知道的nextTick
循環(huán)slots操作類名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
handlerComponent(slot, handler /* addSkeletPrefix | removeSkeletPrefix */ , init) { const originchildren = (((slot.componentInstance || {})._vnode || {}).componentOptions || {}).children const compchildren = ((slot.componentInstance || {})._vnode || {}).children !init && handler(slot) if (compchildren) this .handlerPrefix(compchildren, handler, false ) if (originchildren) this .handlerPrefix(originchildren, handler, false ) }, handlerPrefix(slots, handler, init = true ) { slots.forEach(slot => { var children = slot.children || (slot.componentOptions || {}).children || ((slot.componentInstance || {})._vnode || {}).children if (slot.data) { if (!slot.componentOptions) { !init && handler(slot) } else if (! this .$hoc_utils.getAbstractComponent(slot)) { ;( function (slot) { const handlerComponent = this .handlerComponent.bind( this , slot, handler, init) const insert = (slot.data.hook || {}).insert ;(slot.data.hook || {}).insert = () => { // 函數(shù)重構(gòu), 修改原有的組件hook, 并且保證insert只執(zhí)行一次 insert(slot) handlerComponent() } ;(slot.data.hook || {}).postpatch = handlerComponent }).call( this , slot) } } if (slot && slot.elm && slot.elm.nodeType === 3) { if ( this .showSpin) { slot.memorizedtextContent = slot.elm.textContent slot.elm.textContent = '' } else { slot.elm.textContent = slot.memorizedtextContent || slot.elm.textContent || slot.text } } children && this .handlerPrefix(children, handler, false ) }) }, |
逐步分析:
- 我們遍歷slots插槽
- 獲取當(dāng)前vnode下的children集合以備做下一次循環(huán)
- 判斷是否是原生HTML元素,只有組件vnode才會具備componentOptions屬性
- 判斷是否抽象組件,我們知道抽象組件是不會渲染到真實DOMTree上的,例如keep-alive、transition,每個組件的vnode擁有獨有的hooks生命周期: init(初始化)、insert(插入)、prepatch(更新)、destroy(銷毀),每個生命周期會在不同階段觸發(fā), 劫持insert,保留原有的insert方法,隨后重構(gòu)vnode的insert方法在里面調(diào)用handlerComponent方法進行添加類名,這里與上面的mounted的nextTick用法理念類似,由于handlerComponent需要知道子組件的實例,所以必須在實例化后去調(diào)用,而組件的init方法會實例組件并且直接調(diào)用watcher.update(watcher.render()), 也就是我們在調(diào)用insert方法的時候其實是在update(render())后,所以這里能夠獲取到實例化后子組件
- 判斷nodeType是否是文本節(jié)點,若是的話需要先將textContent保存后進行刪除,保證在骨架屏出現(xiàn)時不會顯示默認文字,在骨架屏消失時,將原先保留的默認文字返回給vnode,這樣就能自由在骨架屏的顯示隱藏期間自由切換
操作vnode的靜態(tài)類名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
addSkeletPrefix(slot) { const rootVnode = slot.componentOptions ? (slot.componentInstance || {})._vnode || {} : slot; if (rootVnode.elm) { rootVnode.elm.classList.add( this .skeletPrefix) } else { ;(rootVnode.data || {}).staticClass += ` ${ this .skeletPrefix}` } }, removeSkeletPrefix(slot) { const rootVnode = slot.componentOptions ? (slot.componentInstance || {})._vnode || {} : slot; if (rootVnode.elm) { rootVnode.elm.classList && rootVnode.elm.classList.remove( this .skeletPrefix) } else if (rootVnode.data.staticClass) { rootVnode.data.staticClass = rootVnode.data.staticClass.replace(` ${ this .skeletPrefix}`, '' ) } } |
addSkeletePrefix用于添加gm-skeleton類名,而removeSkeletonPrefix則是用于刪除gm-skeleton類名
使用方法
1
2
3
4
|
1
2
3
4
5
|
< gm-skeleton > < Component /> < div ></ div > < div >< span >前端馬丁</ span ></ div > </ gm-skeleton > |
傳值
屬性名 | 值 | 描述 |
---|---|---|
showSpin | Boolean | 是否開啟骨架屏,默認為true |
skeletPrefix | String | 骨架屏類名, 默認是gm-skeleton |
效果如下
具體樣式是根據(jù)開發(fā)者自己寫的樣式來生成的,通過gm-skeleton包裹,如上的使用方法,以下是一個簡單的例子
完整地址
以上就是vue實現(xiàn)骨架屏的示例的詳細內(nèi)容,更多關(guān)于vue實現(xiàn)骨架屏的資料請關(guān)注服務(wù)器之家其它相關(guān)文章!
原文鏈接:https://juejin.cn/post/6954245046690381831