use jest create ut for vue component: vue:<template>
  <transition-group
    :tag="tag"
    name="lazy-component"
    style="position: relative;"
    @after-enter="afterEnter"
  >
    <template v-if="!show">
      <div
        v-if="$slots.skeleton"
        key="skeleton"
      >
        <slot name="skeleton" />
      </div>
      <loading
        v-else
        key="loading"
        :loading-style="loadingStyle"
      />
    </template>
    <div v-else key="content">
      <slot />
    </div>
  </transition-group>
</template>

<script>
import loading from './loading'
export default {
  name: 'GdLazyComponent',
  components: {
    loading
  },
  props: {
    tag: {
      type: String,
      default: 'div'
    },
    loadingStyle: {
      type: Object,
      default: () => ({})
    }
  },
  // render: function (h) {
  //   if (this.show === false) {
  //     // 组件选项对象
  //     if (lazy.options.loading) {
  //       return h(lazy.options.loading, null)
  //     } else {
  //       return h(Loading, {
  //         props: {
  //           loadingStyle: this.loadingStyle
  //         }
  //       })
  //     }
  //   }
  //   return h(this.tag, null, this.$slots.default)
  // },
  data() {
    return {
      show: false
    }
  },
  created() {},
  mounted() {
    if (this.gdLazy) {
      this.gdLazy.addLazyInstance(this)
    }
  },
  destroyed() {
    if (this.gdLazy) {
      this.gdLazy.removeLazyInstace(this)
    }
  },
  methods: {
    load() {
      this.show = true
    },
    afterEnter() {
      // 等节点渲染完触发事件show
      this.$nextTick(() => {
        this.$emit('show', this)
      })
    }
  }
}
</script>  install.js: 
require('intersection-observer')

const PRE_LOAD = 1.0

export default (Vue) => {
  return class lazy {
    constructor({ root, preload, lazyFunc }) {
      this.TargetQueue = [] // 待加载的队列
      this.options = {
        // loading: loading,
        root: root,
        callback: lazyFunc,
        rootMargin: ((preload || PRE_LOAD) - 1) * window.innerHeight + 'px'
        // threshold: undefined
      }
      this._initObserver()
    }

    addLazyInstance(vm) {
      if (!vm) return
      // 加入队列
      this.TargetQueue.push(vm)
      // 开始监听
      this._observer.observe(vm.$el)
    }

    removeLazyInstace(vm) {
      let index = this.TargetQueue.findIndex(item => item === vm)
      if (index !== -1) {
        // 停止监听特定一个目标元素
        this._observer.unobserve(vm.$el)
        // 删除
        this.TargetQueue.splice(index, 1)
      }
    }

    _observerCallback(entries) {
      entries.forEach(item => {
        if (item.isIntersecting) {
          let target = this.TargetQueue.find(findItem => findItem.$el === item.target)
          if (target) {
            target.load()
            if (this.options.callback && typeof this.options.callback === 'function') {
              this.options.callback(target)
            }
            this.removeLazyInstace(target)
          }
        }
      })
    }
    // 初始化
    _initObserver() {
      var observerOptions = {
        root: this.options.root,
        rootMargin: this.options.rootMargin
        // threshold: this.options.threshold
      }
      // 创建
      this._observer = new IntersectionObserver(this._observerCallback.bind(this), observerOptions)
    }
  }
}