浏览器【详解】内置Observer(共五种,用于前端监控、图片懒加载、无限滚动、响应式布局、生成安全报告等)

通用语法

浏览器的内置Observer都需要通过 new 来创建实例 ob (自定义名称)

  • 开始/继续监听 ob.observe(targetNode, config)
    • targetNode 目标元素
    • config 配置
  • 暂停监听 ob.disconnect()
  • 停止监听 ob.unobserve(targetNode)

MutationObserver

监听DOM 元素的变化(如节点增删、属性修改、文本内容变化等)

可用于延迟批量处理变化(避免频繁触发回调),提升性能。

js 复制代码
// 选择目标节点
const targetNode = document.getElementById('target');

// 配置观察选项
const config = {
  attributes: true, // 监听属性变化
  childList: true, // 监听子节点增删
  subtree: true // 监听所有后代节点
};

// 回调函数:变化发生时执行
const callback = (mutationsList) => {
  for (const mutation of mutationsList) {
    if (mutation.type === 'childList') {
      console.log('子节点发生变化');
    } else if (mutation.type === 'attributes') {
      console.log(`属性 ${mutation.attributeName} 发生变化`);
    }
  }
};

// 创建观察者实例
const observer = new MutationObserver(callback);

// 开始观察目标节点
observer.observe(targetNode, config);

// 停止观察(必要时)
// observer.disconnect();

交叉观察器 IntersectionObserver

监测目标元素与视口或祖先元素的交叉状态(如元素进入 / 离开视口),常用于懒加载、滚动动画等场景。

  • 高效检测元素可见性,避免频繁触发scroll事件导致的性能问题。
  • 支持配置交叉区域的阈值(如元素可见 50% 时触发)。
js 复制代码
// 创建观察器
let ob = new IntersectionObserver(callback, option);
  • 在元素进入视口和离开视口时,都会触发回调函数 callback
  • callback 的参数是由 相交观察器条目 构成的数组,数组的数量由被观察对象的数量决定。

观察多个对象

c 复制代码
ob.observe(elementA);
ob.observe(elementB);

相交观察器条目

共六个属性

  • time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
  • target:被观察的目标元素,是一个 DOM 节点对象
  • rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
  • boundingClientRect:目标元素的矩形区域的信息
  • intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
  • intersectionRatio:目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0

配置选项

  • threshold 触发的阈值,默认值为 [0] ,即元素一出现即触发
c 复制代码
  threshold: 0.1 // 元素可见10%时触发
c 复制代码
  threshold: [0.5,1] // 元素可见50% 和 100% 时都触发
  • root 指定目标元素所在的容器节点(即根元素),容器元素必须是目标元素的祖先节点。
  • rootMargin 定义根元素的margin,用来扩展或缩小rootBounds这个矩形的大小,从而影响intersectionRect交叉区域的大小。它使用CSS的定义方法,比如10px 20px 30px 40px,表示 top、right、bottom 和 left 四个方向的值。

注意事项

  • IntersectionObserver 是异步的,不随着目标元素的滚动同步触发。
  • IntersectionObserver 的优先级非常低,只在其他任务执行完,浏览器有了空闲才会执行。

实战范例 -- 图片懒加载

js 复制代码
// 目标元素(如图片)
const target = document.querySelector('.lazy-image');

// 配置选项
const options = {
  root: null, // 以视口为参考
  rootMargin: '0px', // 扩展/收缩参考区域
  threshold: 0.1 // 元素可见10%时触发
};

// 回调函数:交叉状态变化时执行
const callback = (entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      // 元素进入视口,加载图片
      entry.target.src = entry.target.dataset.src;
      // 停止观察已加载的元素
      observer.unobserve(entry.target);
    }
  });
};

// 创建观察者实例
const observer = new IntersectionObserver(callback, options);

// 开始观察目标元素
observer.observe(target);

c 复制代码
function query(selector) {
  return Array.from(document.querySelectorAll(selector));
}

var observer = new IntersectionObserver(
  function(changes) {
    changes.forEach(function(change) {
      var container = change.target;
      var content = container.querySelector('template').content;
      container.appendChild(content);
      observer.unobserve(container);
    });
  }
);

query('.lazy-loaded').forEach(function (item) {
  observer.observe(item);
});

实战范例 -- 无限滚动

c 复制代码
var intersectionObserver = new IntersectionObserver(
  function (entries) {
    // 如果不可见,就返回
    if (entries[0].intersectionRatio <= 0) return;
    loadItems(10);
    console.log('Loaded new items');
  });

// 开始观察
intersectionObserver.observe(
  document.querySelector('.scrollerFooter')
);

无限滚动时,最好在页面底部有一个页尾栏(又称sentinels)。一旦页尾栏可见,就表示用户到达了页面底部,从而加载新的条目放在页尾栏前面。这样做的好处是,不需要再一次调用observe()方法,现有的IntersectionObserver可以保持使用。

ResizeObserver

监听元素尺寸变化(如宽度、高度改变),适用于响应式布局、动态调整 UI 等场景。

js 复制代码
// 目标元素
const target = document.querySelector('.resizable-box');

// 回调函数:尺寸变化时执行
const callback = (entries) => {
  for (const entry of entries) {
    const { width, height } = entry.contentRect;
    console.log(`元素尺寸变化:${width}px × ${height}px`);
  }
};

// 创建观察者实例
const observer = new ResizeObserver(callback);

// 开始观察目标元素
observer.observe(target);

// 停止观察(必要时)
// observer.unobserve(target);

PerformanceObserver

监测性能指标数据(如页面加载时间、资源加载性能、长任务等),帮助开发者分析和优化网页性能。

  • 捕获各种性能相关事件(如 navigation、resource、longtask 等)。
  • 异步获取性能数据,不阻塞主线程。
js 复制代码
// 监测长任务(阻塞主线程超过50ms的任务)
const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach(entry => {
    console.log(`长任务持续时间:${entry.duration}ms`);
  });
});

// 开始观察长任务
observer.observe({ type: 'longtask', buffered: true });

ReportingObserver

收集浏览器生成的各种报告(如 CSP 违规、废弃 API 使用警告、干预报告等),帮助开发者监控应用在生产环境中的问题。

  • 捕获浏览器的安全、性能或兼容性相关报告。
  • 支持批量处理和缓冲已发生的报告。
js 复制代码
// 监测废弃API使用和干预报告
const observer = new ReportingObserver((reports) => {
  reports.forEach(report => {
    console.log(`报告类型:${report.type},内容:`, report.body);
  });
}, { types: ['deprecation', 'intervention'], buffered: true });

// 开始观察报告
observer.observe();