vue實(shí)現(xiàn)骨架屏的示例
骨架屏用途
- 作為spa中路由切換的 loading, 結(jié)合組件的生命周期和ajax請(qǐng)求返回的時(shí)機(jī)來使用.( 作為loading 使用)。作為與用戶聯(lián)系最為密切的前端開發(fā)者,用戶體驗(yàn)是最值得關(guān)注的問題。關(guān)于頁面loading狀態(tài)的展示,主流的主要有l(wèi)oading圖和進(jìn)度條兩種。除此之外,越來越多的APP采用了“骨架屏”的方式去展示未加載內(nèi)容,給予了用戶煥然一新的體驗(yàn)。
- 作為首屏渲染的優(yōu)化
Vue架構(gòu)骨架屏
思路大綱
- 定義一個(gè)抽象組件,在抽象組件的render函數(shù)里獲取插槽
- 深度循環(huán)遍歷插槽,將每個(gè)元素都添加上gm-skeleton的類名
- 將vnode textContent預(yù)暫后清空保證骨架屏出現(xiàn)時(shí)不會(huì)出現(xiàn)默認(rèn)文字
- 返回slots
定義一個(gè)抽象組件
什么是抽象組件? 在渲染時(shí)會(huì)被跳過,只做運(yùn)行時(shí)的操作的組件
export default { name: 'GmSkeleton', abstract: true // 抽象組件的屬性 }
獲取插槽并初始化操作骨架屏
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里面, 因?yàn)閔andlerPrefix里需要獲取真實(shí)的DOM,nextTick是用來執(zhí)行排序后的更新隊(duì)列里的所有方法, 在執(zhí)行render前, GMSkeleton組件的renderWatcher已被收集到更新隊(duì)列里,所以此時(shí)定義nextTick CallBack函數(shù)里能獲取到渲染后對(duì)應(yīng)插槽里所有真實(shí)DOM,若是不了解nextTick原理,請(qǐng)移步你不知道的nextTick
循環(huán)slots操作類名
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才會(huì)具備componentOptions屬性
- 判斷是否抽象組件,我們知道抽象組件是不會(huì)渲染到真實(shí)DOMTree上的,例如keep-alive、transition,每個(gè)組件的vnode擁有獨(dú)有的hooks生命周期: init(初始化)、insert(插入)、prepatch(更新)、destroy(銷毀),每個(gè)生命周期會(huì)在不同階段觸發(fā), 劫持insert,保留原有的insert方法,隨后重構(gòu)vnode的insert方法在里面調(diào)用handlerComponent方法進(jìn)行添加類名,這里與上面的mounted的nextTick用法理念類似,由于handlerComponent需要知道子組件的實(shí)例,所以必須在實(shí)例化后去調(diào)用,而組件的init方法會(huì)實(shí)例組件并且直接調(diào)用watcher.update(watcher.render()), 也就是我們?cè)谡{(diào)用insert方法的時(shí)候其實(shí)是在update(render())后,所以這里能夠獲取到實(shí)例化后子組件
- 判斷nodeType是否是文本節(jié)點(diǎn),若是的話需要先將textContent保存后進(jìn)行刪除,保證在骨架屏出現(xiàn)時(shí)不會(huì)顯示默認(rèn)文字,在骨架屏消失時(shí),將原先保留的默認(rèn)文字返回給vnode,這樣就能自由在骨架屏的顯示隱藏期間自由切換
操作vnode的靜態(tài)類名
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類名
使用方法
import Vue from 'vue' import GMSkeleton from 'path/to/GMSkeleton' Vue.use(GMSkeleton)
<gm-skeleton> <Component /> <div></div> <div><span>前端馬丁</span></div> </gm-skeleton>
傳值
屬性名 | 值 | 描述 |
---|---|---|
showSpin | Boolean | 是否開啟骨架屏,默認(rèn)為true |
skeletPrefix | String | 骨架屏類名, 默認(rèn)是gm-skeleton |
效果如下
具體樣式是根據(jù)開發(fā)者自己寫的樣式來生成的,通過gm-skeleton包裹,如上的使用方法,以下是一個(gè)簡(jiǎn)單的例子
完整地址
以上就是vue實(shí)現(xiàn)骨架屏的示例的詳細(xì)內(nèi)容,更多關(guān)于vue實(shí)現(xiàn)骨架屏的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vite多頁面配置項(xiàng)目實(shí)戰(zhàn)
vite官方文檔中有關(guān)于多頁面應(yīng)用模式如果配置的說明,下面這篇文章主要給大家介紹了關(guān)于vite多頁面配置的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04vue element 關(guān)閉當(dāng)前tab 跳轉(zhuǎn)到上一路由操作
這篇文章主要介紹了vue element 關(guān)閉當(dāng)前tab 跳轉(zhuǎn)到上一路由操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-07-07解決前后端分離 vue+springboot 跨域 session+cookie失效問題
這篇文章主要介紹了前后端分離 vue+springboot 跨域 session+cookie失效問題的解決方法,解決過程也很簡(jiǎn)單 ,需要的朋友可以參考下2019-05-05vue使用Echarts設(shè)置數(shù)據(jù)無效問題記錄及解決方法
這篇文章主要介紹了vue使用Echarts設(shè)置數(shù)據(jù)無效問題記錄,本文通過場(chǎng)景分析給大家分享解決方法,需要的朋友可以參考下2022-08-08