Use of frame screen

  • Loading is used as a route switch in a SPA in combination with the component’s life cycle and the timing of an Ajax request’s return. As the front-end developer with the closest connection to the user, the user experience is the most important concern. There are two main ways to display page loading state: loading chart and progress bar. In addition, more and more apps are using “skeleton screens” to display unloaded content, giving users a completely new experience.
  • Optimized as the first screen rendering

Vue framework screen

Outline of thinking

  • Define an abstract component and get the slot in the Render function of the abstract component
  • Deep loops through the slot, adding the gM-skeleton class name to each element
  • Empty the vNode textContent so that the default text does not appear when the skeleton screen appears
  • Returns the slots

Define an abstract component

What is an abstract component? Components that are skipped during rendering and only do runtime operations

    export default {
      name: 'GmSkeleton'.abstract: true // Abstracts the properties of the component
    }
Copy the code

Get the slot and initialize the operation skeleton screen

    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
    }
Copy the code

Here we put the method that handles slots inside of nextTick, because handlerPrefix needs to get the real DOM, NextTick is used to execute all the methods in the sorted update queue. The GMSkeleton renderWatcher has been collected before render is executed. Therefore, defining the nextTick CallBack function at this time can fetch all the real DOM in the corresponding slot after rendering. If you do not know the nextTick principle, please move to nextTick which you do not know

Loop the slots operation class name

    handlerComponent(slot, handler/* addSkeletPrefix | removeSkeletPrefix */, init) {
      const originchildren = (((slot.componentInstance || {})._vnode || {}).componentOptions || {}).children
      constcompchildren = ((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)
              constinsert = (slot.data.hook || {}).insert ; (slot.data.hook || {}).insert =() = > { // Function refactoring to modify the original component hook and ensure that the insert is executed only onceinsert(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)})},Copy the code

Step by step analysis:

  1. We walked through the slots
  2. Gets the children collection under the current vNode for the next loop
  3. To determine if it is a native HTML element, only the component VNode has the componentOptions attribute
  4. Abstract components do not render to the real DOMTree, for example keep-alive and Transition. Each component’s VNode has its own hooks life cycle: Init, INSERT, prepatch, destroy, each lifecycle is triggered at a different stage, hijacking insert, preserving the original insert method, / / Mounted nextTick. / / handlerComponent needs to know the instance of its child component, so it must be called after it is instantiated. The component’s init method instantiates the component and calls watcher.update(watcher.render()) directly, which means we call insert after update(render()), so we get the instantiated child component
  5. Check whether nodeType is a text node. If so, save and delete textContent first to ensure that no default text will be displayed when the skeleton screen appears. When the skeleton screen disappears, return the default text to VNode, so that it can freely switch between the skeleton screen and hidden

The static class name that operates on vNode

    addSkeletPrefix(slot) {
      const rootVnode = slot.componentOptions ? (slot.componentInstance || {})._vnode || {} : slot;
      if (rootVnode.elm) {
        rootVnode.elm.classList.add(this.skeletPrefix)
      } else {
        ;(rootVnode.data || {}).staticClass += ` The ${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(` The ${this.skeletPrefix}`.' ')}}Copy the code

AddSkeletePrefix is used to add the GM-skeleton name and removeSkeletonPrefix is used to remove the GM-skeleton name

Method of use

  import Vue from 'vue'
  import GMSkeleton from 'path/to/GMSkeleton'
  
  Vue.use(GMSkeleton)
Copy the code
  <gm-skeleton>
    <Component />
    <div></div>
    <div><span>The front-end Martin</span></div>
  </gm-skeleton>
Copy the code

The value of

The property name value describe
showSpin Boolean Whether to enable skeleton screen. The default value is true
skeletPrefix String Skeleton screen class name, default is GM-skeleton

Results the following

The specific styles are generated according to the styles written by the developers themselves, via the GM-skeleton package, as used above, here is a simple example

Complete address

80 lines of code to implement the Vue skeleton screen

conclusion

Hope to click a like, to a concern hee hee

Wechat id: IAmFineThanksMartin