vue實現(xiàn)骨架屏的示例
什么是抽象組件? 在渲染時會被跳過,只做運行時的操作的組件
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里面, 因為handlerPrefix里需要獲取真實的DOM,nextTick是用來執(zhí)行排序后的更新隊列里的所有方法, 在執(zhí)行render前, GMSkeleton組件的renderWatcher已被收集到更新隊列里,所以此時定義nextTick CallBack函數(shù)里能獲取到渲染后對應插槽里所有真實DOM,若是不了解nextTick原理,請移步你不知道的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 || {}).childrenif (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ù)重構, 修改原有的組件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插槽 獲取當前vnode下的children集合以備做下一次循環(huán) 判斷是否是原生HTML元素,只有組件vnode才會具備componentOptions屬性 判斷是否抽象組件,我們知道抽象組件是不會渲染到真實DOMTree上的,例如keep-alive、transition,每個組件的vnode擁有獨有的hooks生命周期: init(初始化)、insert(插入)、prepatch(更新)、destroy(銷毀),每個生命周期會在不同階段觸發(fā), 劫持insert,保留原有的insert方法,隨后重構vnode的insert方法在里面調用handlerComponent方法進行添加類名,這里與上面的mounted的nextTick用法理念類似,由于handlerComponent需要知道子組件的實例,所以必須在實例化后去調用,而組件的init方法會實例組件并且直接調用watcher.update(watcher.render()), 也就是我們在調用insert方法的時候其實是在update(render())后,所以這里能夠獲取到實例化后子組件 判斷nodeType是否是文本節(jié)點,若是的話需要先將textContent保存后進行刪除,保證在骨架屏出現(xià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 是否開啟骨架屏,默認為true skeletPrefix String 骨架屏類名, 默認是gm-skeleton
效果如下具體樣式是根據(jù)開發(fā)者自己寫的樣式來生成的,通過gm-skeleton包裹,如上的使用方法,以下是一個簡單的例子
80行代碼實現(xiàn)Vue骨架屏
以上就是vue實現(xiàn)骨架屏的示例的詳細內容,更多關于vue實現(xiàn)骨架屏的資料請關注好吧啦網(wǎng)其它相關文章!
相關文章: