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
。防抖与节流
观察器的回调函数可能会频繁触发,需要根据实际情况进行防抖或节流处理。