关于 vue-use 中 useIntersectionObserver

1,988 阅读1分钟

vrgrr.jpg

useIntersectionObserver

介绍

当 目标元素 与 祖先元素进行了 交叉操作的时候,可以触发该操作
以前是用的是 scroll 事件,监听 目标元素 OffsetTop 是否小于clinetHeight
滚动事件触发过于频繁,需要 节流,现在使用 Intersection Observer
Intersection Observer 中的 Intersection 中文翻译为 交叉,
Observer 翻译为观察者
字面意思理解是 交叉的观察者

来自 mdn 的详细 介绍 Intersection Observer,不在赘述

vueuse 中的使用 方式

<div ref="target">
  <h1>Hello world</h1>
</div>
import { ref } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'

export default {
  setup() {
    const target = ref(null)
    const targetIsVisible = ref(false)

    const { stop } = useIntersectionObserver(
      target,
      ([{ isIntersecting }], observerElement) => {
        targetIsVisible.value = isIntersecting
      },
    )

    return {
      target,
      targetIsVisible,
    }
  },
}

api介绍

    1. useIntersectionObserver

useIntersectionObserver 接受一个 target ,target 指向 这个 Dom 或者 是组件 第二个参数是一个回调函数,有两个参数,一般情况下 只需要第一个

([{ isIntersecting }], observerElement) => {
        targetIsVisible.value = isIntersecting
},

第一个参数的是一个数组对象,因为 可以监控 (返回的实例中有 observe 可以监听)多个元素 mdn

isIntersecting,表示是否在可视返回内
intersectionRatio 交叉比例
    1. observerElement 目标元素一般情况用不到

vueuse 的源码实现

        // noop 为一个空函数
          let cleanup = noop
          
          // isSupported 是内部的一个判断,是否支持
          
	  const stopWatch = isSupported
	    ? watch(
	      () => ({
	        el: unrefElement(target),
	        root: unrefElement(root),
	      }),
	      ({ el, root }) => {
            // 把上一次的清除掉
	        cleanup()

	        if (!el)
	          return
	
	        // @ts-expect-error missing type
	        const observer = new window.IntersectionObserver(
	          callback,
	          {
	            root,
	            rootMargin,
	            threshold,
	          },
	        )
	        observer.observe(el)

	        cleanup = () => {
	          observer.disconnect()
	          cleanup = noop
	        }
	      },
              
// flush 为每个更改触发监听器,也可以使用 await nextTick()
	      { immediate: true, flush: 'post' },
	    )
	    : noop
	
// 由于 cleanup 是全局变量
	  const stop = () => {
	    cleanup()
	    stopWatch()
	  }

unrefElement 要单独拿出来,因为 target 可能是 vue 的组件或者是一个 dom,需要解包

// 先对 plain 尽心 unref,如果存在 $el,证明是个 vue 组件,否则就是一个普通的标签
function unrefElement(elRef: MaybeElementRef) {
  const plain = unref(elRef)
  return (plain as VueInstance)?.$el ?? plain
}
    import { useIntersectionObserver,MaybeRef } from "@vueuse/core";
    type VueInstance = InstanceType<ReturnType<typeof defineComponent>>
    type MaybeElementRef = MaybeRef<Element | VueInstance | undefined | null>