前端五大 Observer 你都掌握了吗? (一)

4,310 阅读4分钟

在前端开发中,常见的 Observer 机制包括 MutationObserver(监听 DOM 结构变化)、IntersectionObserver(监测元素进入视口情况)、ResizeObserver(检测元素尺寸变化)、PerformanceObserver(监控性能指标)、以及 ReportingObserver(捕获和报告浏览器中的弃用警告和错误报告)。这些 Observer 工具可以有效地处理异步事件、状态变化和性能监控。

今天,我们将深入探讨其中一个重要的 Observer 工具——MutationObserver。

它是一种高效的机制,用于监控 DOM 树的变化,包括节点的添加、删除和属性的修改。MutationObserver 的出现不仅优化了对 DOM 变更的响应方式,还避免了传统轮询方法带来的性能问题。让我们一起了解 MutationObserver 的设计初衷、应用场景、基本用法及其在前端开发中的实际应用,掌握如何利用它来提升你的开发效率和应用性能。

MutationObserver: 高效的 DOM 变更监控工具

概述

MutationObserver 是一个浏览器提供的 API,用于监视 DOM 树的变化,包括节点的添加、删除和属性的修改。它旨在替代传统的轮询方式,通过异步回调的方式高效地检测和响应 DOM 变更,避免了性能问题和复杂的实现细节。

MutationObserver 旨在替代旧有的 Mutation Events 以提高性能,它通过异步批处理 DOM 变更,减少了对浏览器性能的影响。与 Mutation Events 不同,MutationObserver 能够精准地监控特定的 DOM 变化类型,避免重复触发事件,提供更高效、灵活的变更检测机制,适用于需要高性能和细粒度监控的场景。

应用场景

  • 动态内容监控:实时检测和响应页面中的动态内容变化,如新增、删除或修改元素。
  • 表单输入监控:跟踪用户在动态表单中的输入变化,用于实时验证或更新表单状态。
  • 自动更新用户界面:在用户界面上自动更新视图或处理变化,如动态加载的数据或实时反馈。
  • 实现自定义组件:在组件内部检测和响应子组件或 DOM 结构的变化,以便进行相应的渲染或处理。

如何创建一个 MutationObserver 实例

image.png

  1. 创建一个 MutationObserver 实例

// 创建一个 MutationObserver 实例
const observer = new MutationObserver((mutationsList, observer) => {
  // 遍历所有变动记录
  // mutationsList 是一个 MutationRecord 的数组,包含所有发生的 DOM 变化
  for (const mutation of mutationsList) {
   // 打印每个 MutationRecord 对象的信息
    console.log(mutation);
    // 处理每个变动
    console.log('变动类型:', mutation.type);
    if (mutation.type === 'attributes') {
      console.log(`属性变动 - 属性名: ${mutation.attributeName}, 变动前值: ${mutation.oldValue}`);
    } else if (mutation.type === 'childList') {
      console.log('子节点变动 - 添加的节点:', mutation.addedNodes, '删除的节点:', mutation.removedNodes);
    } else if (mutation.type === 'characterData') {
      console.log(`文本内容变动 - 变动前值: ${mutation.oldValue}`);
    }
  }
});

MutationRecord 对象是 MutationObserver 在监听到 DOM 变动时提供的记录对象,包含了有关 DOM 变化的详细信息。主要包括以下属性:

  • type: 变动的类型。可能的值包括 attributes(属性变动)、characterData(文本内容变动)、childList(子节点变动)。
  • target: 发生变动的目标 DOM 节点。
  • addedNodes: 被添加到 DOM 的节点列表。返回的是一个 NodeList,如果没有添加节点则为空。
  • removedNodes: 从 DOM 中移除的节点列表。返回的是一个 NodeList,如果没有移除节点则为空。
  • previousSibling: 变动前目标节点的前一个兄弟节点(对于 childList 类型变动有效)。
  • nextSibling: 变动前目标节点的后一个兄弟节点(对于 childList 类型变动有效)。
  • attributeName: 被修改的属性名称(对于 attributes 类型变动有效)。
  • attributeNamespace: 被修改属性的命名空间(对于 attributes 类型变动有效)。
  • oldValue: 变动前的属性值(对于 attributes 和 characterData 类型变动有效)。
  1. 定义观察的配置选项

const config = {
  attributes: true,            // 观察属性的变化
  attributeFilter: ['class'],  // 只观察 class 属性的变化
  attributeOldValue: true,     // 获取变动前的属性值
  childList: true,             // 观察子节点的变化
  subtree: true,               // 观察所有后代节点
  characterData: true,         // 观察文本内容的变化
  characterDataOldValue: true  // 获取变动前的文本内容
};
  1. 选择要观察的 DOM 元素

const targetNode = document.getElementById('target');
  1. 启动观察

observer.observe(targetNode, config);
  1. 停止观察 (vue unmounted 和 react useEffect 清除副作用)

observer.disconnect();

注意事项

使用 MutationObserver 时需要注意几个潜在的缺点和注意事项。首先,MutationObserver 可能会引发性能问题,尤其是在观察大量 DOM 变更时,因为每次变更都会触发回调函数。其次,由于 MutationObserver 会观察到子节点和后代节点的变更,这可能导致不必要的回调触发,因此要谨慎配置观察范围。最后,确保正确处理回调函数中的变更记录,以避免因操作不当而引发额外的性能问题或错误。