ResizeObserver 相关问题解决方案

ResizeObserver 相关问题解决方案----错误抑制

一、问题背景

在使用 ResizeObserver 监测元素尺寸变化时(尤其在 Element Plus 等 UI 组件库中),常出现以下错误:

  • ResizeObserver loop completed with undelivered notifications:因元素尺寸快速连续变化,浏览器无法及时处理所有尺寸变化通知,导致循环错误
  • 关联错误如 Cannot convert object to primitive value:可能伴随 ResizeObserver 回调执行异常出现

这些错误通常不影响功能运行,但会污染控制台输出,干扰开发调试。

二、解决方案核心思路

通过拦截错误处理机制、包装 ResizeObserver 实例,实现对特定错误的抑制,同时不影响其他正常错误的监控。

三、具体实现方案

1. 错误识别规则

定义错误过滤函数,精准匹配需要忽略的错误特征:

javascript

运行

const shouldIgnoreError = (message) => {
  if (typeof message !== 'string') return false
  return message.includes('ResizeObserver') || 
         message.includes('loop completed with undelivered notifications') ||
         message.includes('Cannot convert object to primitive value')
}

2. 重写全局错误处理

  • 覆盖 window.onerror:拦截页面级错误,符合规则则阻止默认处理

    javascript

    运行

    const originalOnError = window.onerror
    window.onerror = function(message) {
      if (shouldIgnoreError(message)) return true
      return originalOnError ? originalOnError.apply(this, arguments) : false
    }
    
  • 拦截控制台输出:过滤 console.error 和 console.warn 中的目标错误

    javascript

    运行

    const originalConsoleError = console.error
    console.error = function(...args) {
      const message = args.join(' ')
      if (!shouldIgnoreError(message)) {
        originalConsoleError.apply(console, args)
      }
    }
    
    // console.warn 处理逻辑与 error 一致,略
    

3. 监听错误事件

  • 捕获 error 事件:通过事件冒泡机制拦截错误,阻止传播

    javascript

    运行

    window.addEventListener('error', (e) => {
      if (shouldIgnoreError(e.message)) {
        e.stopImmediatePropagation()
        e.preventDefault()
      }
    }, true)
    
  • 处理未捕获的 Promise 异常:拦截 Promise 链中抛出的相关错误

    javascript

    运行

    window.addEventListener('unhandledrejection', (e) => {
      if (e.reason && shouldIgnoreError(e.reason.message)) {
        e.preventDefault()
        e.stopPropagation()
      }
    })
    

4. 包装 ResizeObserver 构造函数

通过延迟执行回调避免同步循环错误:

javascript

运行

if (typeof ResizeObserver !== 'undefined') {
  const originalResizeObserver = ResizeObserver
  window.ResizeObserver = class extends originalResizeObserver {
    constructor(callback) {
      super((entries, observer) => {
        // 用 setTimeout 打破同步循环,避免触发浏览器异常
        setTimeout(() => {
          try {
            callback(entries, observer)
          } catch (error) {
            if (!shouldIgnoreError(error.message)) {
              originalConsoleError.call(console, error)
            }
          }
        }, 0)
      })
    }
  }
}

四、使用方法

  1. 引入工具模块并执行初始化(模块加载时已自动生效核心逻辑):

    javascript

    运行

    import { suppressResizeObserverErrors } from './resizeObserverFix.js'
    suppressResizeObserverErrors() // 控制台输出"ResizeObserver 错误抑制已启用"
    
  2. 无需修改原有 ResizeObserver 使用方式,工具会自动拦截处理错误。

五、注意事项

  1. 本方案为错误抑制而非根本修复,适用于错误不影响功能的场景
  2. 若需彻底解决,需排查元素尺寸频繁变化的根源(如布局抖动)
  3. 避免过度依赖:长期应关注浏览器或组件库对 ResizeObserver 实现的优化
  4. 可能影响对目标错误的调试,开发阶段可暂时禁用以定位问题

六、参考代码

/**
 * ResizeObserver 错误抑制工具
 * 用于解决 Element Plus 等 UI 组件库中常见的 ResizeObserver 相关错误:
 * - ResizeObserver loop completed with undelivered notifications
 * - Cannot convert object to primitive value
 * 这些错误通常不影响功能但会污染控制台,本工具通过拦截错误处理机制实现精准过滤
 */

// 保存浏览器原生的错误处理方法,用于后续需要时调用原始逻辑
const originalOnError = window.onerror; // 保存原始window.onerror错误处理函数
const originalConsoleError = console.error; // 保存原始console.error输出方法
const originalConsoleWarn = console.warn; // 保存原始console.warn输出方法

/**
 * 判断错误信息是否为需要忽略的 ResizeObserver 相关错误
 * @param {string} message - 错误信息字符串
 * @returns {boolean} 是否需要忽略该错误
 */
const shouldIgnoreError = (message) => {
  // 非字符串类型的错误信息直接返回不忽略
  if (typeof message !== 'string') return false;
  // 匹配 ResizeObserver 相关的特征错误信息
  return message.includes('ResizeObserver') || 
         message.includes('loop completed with undelivered notifications') || // 特定循环错误
         message.includes('Cannot convert object to primitive value'); // 关联转换错误
};

/**
 * 重写 window.onerror 全局错误处理
 * 用于拦截页面级同步错误,对目标错误进行抑制
 */
window.onerror = function(message) {
  // 如果是需要忽略的错误,返回true表示已处理(阻止浏览器默认报错)
  if (shouldIgnoreError(message)) {
    return true; // 阻止默认错误处理
  }
  // 非目标错误,调用原始onerror方法保持原有逻辑
  if (originalOnError) {
    return originalOnError.apply(this, arguments);
  }
  return false;
};

/**
 * 重写 console.error 方法
 * 用于过滤控制台输出的目标错误信息
 */
console.error = function(...args) {
  // 将控制台参数拼接为字符串,便于错误识别
  const message = args.join(' ');
  // 如果是目标错误,不输出到控制台
  if (shouldIgnoreError(message)) {
    return; // 完全忽略这些错误的控制台输出
  }
  // 非目标错误,调用原始console.error输出
  originalConsoleError.apply(console, args);
};

/**
 * 重写 console.warn 方法
 * 用于过滤控制台输出的目标警告信息
 */
console.warn = function(...args) {
  const message = args.join(' ');
  if (shouldIgnoreError(message)) {
    return; // 忽略目标警告
  }
  originalConsoleWarn.apply(console, args);
};

/**
 * 监听全局 error 事件(捕获阶段)
 * 补充拦截通过事件冒泡传递的错误,确保全面性
 * 第三个参数true表示在捕获阶段处理,更早拦截错误
 */
window.addEventListener('error', (e) => {
  if (shouldIgnoreError(e.message)) {
    e.stopImmediatePropagation(); // 阻止事件继续传播给其他监听器
    e.preventDefault(); // 阻止默认处理
    return false;
  }
}, true);

/**
 * 监听未处理的 Promise 拒绝事件
 * 处理 Promise 链中未捕获的目标错误,避免控制台报错
 */
window.addEventListener('unhandledrejection', (e) => {
  // 检查 Promise 拒绝原因中的错误信息
  if (e.reason && shouldIgnoreError(e.reason.message)) {
    e.preventDefault(); // 阻止浏览器默认处理(如控制台输出)
    e.stopPropagation(); // 阻止事件传播
    return false;
  }
});

/**
 * 包装 ResizeObserver 构造函数
 * 通过延迟执行回调打破同步循环,从根源减少错误触发
 */
if (typeof ResizeObserver !== 'undefined') { // 兼容不支持ResizeObserver的环境
  const originalResizeObserver = ResizeObserver; // 保存原生构造函数
  // 自定义ResizeObserver类继承原生类
  window.ResizeObserver = class extends originalResizeObserver {
    /**
     * 重写构造函数,包装回调函数
     * @param {Function} callback - 尺寸变化时的回调函数
     */
    constructor(callback) {
      // 调用父类构造函数,传入包装后的回调
      super((entries, observer) => {
        // 使用setTimeout将回调延迟到下一个事件循环执行
        // 目的:打破同步尺寸变化导致的循环通知,避免浏览器抛出异常
        setTimeout(() => {
          try {
            // 执行用户传入的原始回调
            callback(entries, observer);
          } catch (error) {
            // 回调执行中发生的错误,同样进行过滤
            if (shouldIgnoreError(error.message)) {
              return; // 静默忽略
            }
            // 非目标错误,使用原始console.error输出
            originalConsoleError.call(console, error);
          }
        }, 0);
      });
    }
  };
}

/**
 * 工具初始化函数
 * 模块加载时已自动执行核心逻辑,此函数用于标识工具已启用
 */
export const suppressResizeObserverErrors = () => {
  console.log('ResizeObserver 错误抑制已启用');
};

// 默认导出工具对象
export default {
  suppressResizeObserverErrors
};

通过以上方案,可有效消除 ResizeObserver 相关的冗余错误输出,提升开发体验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值