Skip to content

useObserver

功能概述

实时监控元素的多种状态,包括:

  • DOM 变化(MutationObserver):自动追踪 DOM 结构变动,适合处理动态内容更新。
  • 可视状态(IntersectionObserver):检测元素是否进入视口,实现懒加载或滚动触发效果。
  • 尺寸变化(ResizeObserver):监听元素大小的变化,助力响应式设计。

适用于需要监控页面中元素状态变动的各种场景,比如内容编辑器、懒加载图片、动态布局调整等。


基础用法

vue
<script setup lang="ts">
import { ref } from 'vue'
import { useObserver } from '@/hooks/useObserver'

const container = ref<HTMLElement | null>(null)

const { mutation, intersection, resize, isIntersecting } = useObserver(container, {
  mutation: { enabled: true, callback: (entries) => console.log('DOM 变化记录:', entries) },
  intersection: { enabled: true, callback: (entries) => console.log('可见性记录:', entries) },
  resize: { enabled: true, callback: (entries) => console.log('尺寸变化记录:', entries) }
})
</script>

<template>
  <div ref="container">观察这个元素的状态变化!</div>
</template>

示例组件

Mutation 观察示例

查看代码
vue
<template>
  <a-card style="border-radius: 8px; margin: 16px">
    <a-space>
      <a-button type="primary" shape="round" size="large" @click="text += ' 变化'">修改文本</a-button>
      <a-button type="primary" shape="round" size="large" @click="addChildDom">添加 子DOM</a-button>
    </a-space>
    <div
      ref="container"
      style="margin: 16px 0; border: 1px solid var(--vp-c-text-1); border-radius: 8px; padding: 16px"
    >
      {{ text }}
    </div>
  </a-card>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { useObserver } from '@cp-vuedir/core'
import { Message } from '@arco-design/web-vue'

const container = ref<HTMLElement | null>(null)
const text = ref('初始文本')

const addChildDom = () => {
  const span = document.createElement('span')
  Object.assign(span.style, {
    padding: '4px 8px'
  })
  span.innerText = `新增的 DOM ${(Math.random() * 50 + 50).toFixed(2)}`
  container.value?.appendChild(span)
}

useObserver(container, {
  mutation: { enabled: true, callback: () => Message.success('DOM 变化:新增了DOM内容') }
})
</script>

Intersection 观察示例

查看代码
vue
<template>
  <a-card style="padding: 8px; border-radius: 8px">
    <p>是否可见:{{ isIntersecting }}</p>
    <h3>Intersection Observer 示例</h3>
    <a-button ref="target" type="primary" shape="round" size="large" long status="warning" style="margin-top: 30vh">
      滚动到这里触发观察
    </a-button>
  </a-card>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { useObserver } from '@cp-vuedir/core'
import { Notification } from '@arco-design/web-vue'

const target = ref<HTMLElement | null>(null)
const { isIntersecting } = useObserver(target, {
  intersection: {
    enabled: true,
    callback: (entries) =>
      Notification.info({
        title: 'Intersection Observer 示例',
        content: `是否可见:${entries[0].isIntersecting ? '是' : '否'}`
      })
  }
})
</script>

Resize 观察示例

查看代码
vue
<template>
  <div>
    <h3>Resize Observer 示例</h3>
    <div
      ref="box"
      style="
        width: 200px;
        height: 100px;
        border: 1px solid var(--vp-c-text-1);
        resize: both;
        overflow: hidden;
        border-radius: 8px;
        display: flex;
        justify-content: center;
        align-items: center;
        cursor: nwse-resize;
        font-size: larger;
      "
    >
      调整大小
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { useObserver } from '@cp-vuedir/core'
import { Notification } from '@arco-design/web-vue'

const box = ref<HTMLElement | null>(null)
useObserver(box, {
  resize: {
    enabled: true,
    callback: (entries) =>
      Notification.warning({
        title: 'Resize Observer 示例',
        content: `宽度:${entries[0].contentRect.width}px,高度:${entries[0].contentRect.height}px`,
        showIcon: false
      })
  }
})
</script>

组合 Observer 示例

查看代码
vue
<template>
  <a-card style="border-radius: 8px; margin: 16px; padding: 16px">
    <a-space>
      <a-button type="primary" shape="round" size="large" @click="text += ' 变化'">修改文本</a-button>
      <a-button type="primary" shape="round" size="large" @click="addChildDom">添加子 DOM</a-button>
    </a-space>
    <div
      ref="target"
      style="
        margin-top: 30vh;
        width: 200px;
        height: 100px;
        border: 1px solid var(--vp-c-text-1);
        resize: both;
        overflow: hidden;
        border-radius: 8px;
        display: flex;
        justify-content: center;
        align-items: center;
        cursor: nwse-resize;
        font-size: larger;
      "
    >
      {{ text }}
    </div>
  </a-card>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import { useObserver } from '@cp-vuedir/core'
import { Message, Notification } from '@arco-design/web-vue'

const target = ref<HTMLElement | null>(null)
const text = ref('初始文本')

const addChildDom = () => {
  const span = document.createElement('span')
  Object.assign(span.style, {
    padding: '4px 8px'
  })
  span.innerText = '新增的 DOM'
  target.value?.appendChild(span)
}

const { isIntersecting } = useObserver(target, {
  intersection: {
    enabled: true,
    callback: (entries) =>
      Notification.info({
        title: 'Intersection Observer',
        content: `是否可见:${entries[0].isIntersecting ? '是' : '否'}`
      })
  },
  mutation: {
    enabled: true,
    callback: () => Message.success('DOM 变化')
  },
  resize: {
    enabled: true,
    callback: (entries) =>
      Notification.warning({
        title: 'Resize Observer',
        content: `宽度:${entries[0].contentRect.width}px,高度:${entries[0].contentRect.height}px`,
        showIcon: false
      })
  }
})

watch(isIntersecting, (val) => {
  Message.info({
    content: `综合案例中,元素是否可见:${val ? '是' : '否'}`
  })
})
</script>

适用场景

  • 动态内容更新监控
    当 DOM 结构发生变化时触发回调,如实时预览编辑器内容。

  • 懒加载及滚动触发
    利用 IntersectionObserver 判断元素是否进入视口,优化加载性能(懒加载图片,滚动加载更多内容)。

  • 响应式布局调整
    ResizeObserver 让你在元素尺寸变化时及时调整布局,保证页面响应式体验。


API

属性名说明类型是否必选默认值
mutation
存储 MutationObserver 的回调记录
Ref<MutationRecord[]>
[]
intersection
存储 IntersectionObserver 的回调记录
Ref<IntersectionObserverEntry[]>
[]
resize
存储 ResizeObserver 的回调记录
Ref<ResizeObserverEntry[]>
[]
isIntersecting
当前元素是否处于视口内
Ref<boolean>
false
disconnect
断开所有观察器连接,停止监控
Function
-
reconnect
重新连接观察器,恢复监控
Function
-

注意事项

注意

  • 生命周期管理
    观察器会在组件卸载时自动断开连接,但在复杂场景中,建议主动调用 disconnect 以防内存泄漏。

  • 浏览器兼容性
    部分 API 在老版本浏览器中可能不支持,务必检查目标浏览器的兼容情况。

  • 性能优化
    开启多个观察器会对性能产生影响,建议按需使用或在适当时机调用 disconnect

  • 防抖与节流

    观察器的回调函数可能会频繁触发,需要根据实际情况进行防抖或节流处理。