好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

vue实现骨架屏的示例

骨架屏用途 作为spa中路由切换的 loading, 结合组件的生命周期和ajax请求返回的时机来使用.( 作为loading 使用)。作为与用户联系最为密切的前端开发者,用户体验是最值得关注的问题。关于页面loading状态的展示,主流的主要有loading图和进度条两种。除此之外,越来越多的APP采用了[骨架屏]的方式去展示未加载内容,给予了用户焕然一新的体验。 作为首屏渲染的优化

Vue架构骨架屏

思路大纲 定义一个抽象组件,在抽象组件的render函数里获取插槽 深度循环遍历插槽,将每个元素都添加上gm-skeleton的类名 将vnode textContent预暂后清空保证骨架屏出现时不会出现默认文字 返回slots

定义一个抽象组件

什么是抽象组件? 在渲染时会被跳过,只做运行时的操作的组件

?

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是用来执行排序后的更新队列里的所有方法, 在执行render前, GMSkeleton组件的renderWatcher已被收集到更新队列里,所以此时定义nextTick CallBack函数里能获取到渲染后对应插槽里所有真实DOM,若是不了解nextTick原理,请移步 你不知道的nextTick

循环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 = () => { // 函数重构, 修改原有的组件hook, 并且保证insert只执行一次

             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集合以备做下一次循环 判断是否是原生HTML元素,只有组件vnode才会具备componentOptions属性 判断是否抽象组件,我们知道抽象组件是不会渲染到真实DOMTree上的,例如keep-alive、transition,每个组件的vnode拥有独有的hooks生命周期: init(初始化)、insert(插入)、prepatch(更新)、destroy(销毁),每个生命周期会在不同阶段触发, 劫持insert,保留原有的insert方法,随后重构vnode的insert方法在里面调用handlerComponent方法进行添加类名,这里与上面的mounted的nextTick用法理念类似,由于handlerComponent需要知道子组件的实例,所以必须在实例化后去调用,而组件的init方法会实例组件并且直接调用watcher.update(watcher.render()), 也就是我们在调用insert方法的时候其实是在update(render())后,所以这里能够获取到实例化后子组件 判断nodeType是否是文本节点,若是的话需要先将textContent保存后进行删除,保证在骨架屏出现时不会显示默认文字,在骨架屏消失时,将原先保留的默认文字返回给vnode,这样就能自由在骨架屏的显示隐藏期间自由切换

操作vnode的静态类名

?

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

 

效果如下

具体样式是根据开发者自己写的样式来生成的,通过gm-skeleton包裹,如上的使用方法,以下是一个简单的例子

完整地址

80行代码实现Vue骨架屏

以上就是vue实现骨架屏的示例的详细内容,更多关于vue实现骨架屏的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/6954245046690381831

查看更多关于vue实现骨架屏的示例的详细内容...

  阅读:37次