超出ResizeObserver循环限制

大约两个月前,我们开始使用Rollbar来通知我们Web应用程序中的各种错误。从那以后,我们偶尔会遇到一些错误:

ResizeObserver loop limit exceeded

让我困惑的事情是,我们没有使用ResizeObserver,我已经调查了唯一的插件,我认为可能是罪魁祸首,即:

Aurelia Resize

但它似乎也没有使用ResizeObserver

同样令人困惑的是,这些错误消息自1月份以来就已经出现,但ResizeObserver支持最近才添加到Chrome 65。

给出这个错误的浏览器版本是:

  • Chrome: 63.0.3239 (ResizeObserver循环限制超过)
  • Chrome: 64.0.3282 (ResizeObserver循环限制超过)
  • Edge: 14.14393 (SecurityError)
  • Edge: 15.15063 (SecurityError)

所以我想知道这是否可能是浏览器漏洞?或者可能是一个实际上与ResizeObserver无关的错误?

165868 次浏览

您可以安全地忽略这个错误。

一个规范的作者在评论中对你的问题写了一个评论,但它不是一个答案,在评论中不清楚答案是否真的是这个线程中最重要的一个,并且是一个让我在我们的哨兵日志中很舒服地忽略它的答案。

This错误意味着ResizeObserver不能在一个动画帧中传递所有的观察结果。它是良性的(您的站点不会崩溃)。- 亚历山大Totic 4月15日3:14

在规范存储库中也有一些相关问题

这是一个老问题,但它仍然可能对某人有帮助。可以通过在requestAnimationFrame中包装回调来避免此错误。 例如:< / p >
const resizeObserver = new ResizeObserver(entries => {
// We wrap it in requestAnimationFrame to avoid this error - ResizeObserver loop limit exceeded
window.requestAnimationFrame(() => {
if (!Array.isArray(entries) || !entries.length) {
return;
}
// your code
});
});

我们也有同样的问题。我们发现chrome扩展是罪魁祸首。具体来说,织机铬扩展是导致错误(或我们的代码与织机扩展的一些交互作用)。当我们禁用扩展,我们的应用程序工作。

我建议禁用某些扩展/插件,看看其中是否有一个可能导致错误。

如果您正在使用Cypress,遇到这个问题,您可以在support/index.js或commands.ts中使用以下代码安全地忽略它

const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/
Cypress.on('uncaught:exception', (err) => {
/* returning false here prevents Cypress from failing the test */
if (resizeObserverLoopErrRe.test(err.message)) {
return false
}
})

你可以跟着讨论在这里。 由于Cypress维护者自己提出了这个解决方案,所以我相信这样做是安全的

添加debounce

new ResizeObserver(_.debounce(entries =>{}, 200);

为我修正了这个错误

摩卡用户:

下面的代码片段覆盖了窗口。Onerror钩子mocha安装并将错误转换为警告。 https://github.com/mochajs/mocha/blob/667e9a21c10649185e92b319006cea5eb8d61f31/browser-entry.js#L74 < / p >

// ignore ResizeObserver loop limit exceeded
// this is ok in several scenarios according to
// https://github.com/WICG/resize-observer/issues/38
before(() => {
// called before any tests are run
const e = window.onerror;
window.onerror = function(err) {
if(err === 'ResizeObserver loop limit exceeded') {
console.warn('Ignored: ResizeObserver loop limit exceeded');
return false;
} else {
return e(...arguments);
}
}
});

不知道有没有更好的办法。

https://github1s.com/chromium/chromium/blob/master/third_party/blink/renderer/core/resize_observer/resize_observer_controller.cc#L44-L45 https://github1s.com/chromium/chromium/blob/master/third_party/blink/renderer/core/frame/local_frame_view.cc#L2211-L2212 < / p >

在查看源代码后,在我的案例中,问题似乎是在调用NotifyResizeObservers函数时出现的,并且没有注册的观察者。

如果没有观察者,GatherObservations函数将返回min_depth为4096,在这种情况下,我们将得到“ResizeObserver循环限制超出”。错误。

我解决这个问题的方法是在页面的整个生命周期中设置一个观察者。

在我的例子中,问题“ResizeObserver -循环限制超出”;被触发是因为window.addEventListener("resize"和React的React.useState

详细地说,我正在处理名为useWindowResize的钩子,用例是这样的const [windowWidth, windowHeight] = useWindowResize();

代码通过useEffect. c对windowWidth / windowHeight更改作出反应。

React.useEffect(() => {
ViewportService.dynamicDimensionControlledBy(
"height",
{ windowWidth, windowHeight },
widgetModalRef.current,
{ bottom: chartTitleHeight },
false,
({ h }) => setWidgetHeight(h),
);
}, [windowWidth, windowHeight, widgetModalRef, chartTitleHeight]);

所以任何浏览器窗口大小的调整都会导致这个问题。

我发现由于连接old-javascript-world (DOM操作,浏览器的事件)和new-javascript-world (React)引起的许多类似问题可能由setTimeout解决,但我将避免它,并在可能的情况下将其称为反模式。

所以我的修复是将setter方法包装到setTimeout函数中。

React.useEffect(() => {
ViewportService.dynamicDimensionControlledBy(
"height",
{ windowWidth, windowHeight },
widgetModalRef.current,
{ bottom: chartTitleHeight },
false,
({ h }) => setTimeout(() => setWidgetHeight(h), 0),
);
}, [windowWidth, windowHeight, widgetModalRef, chartTitleHeight]);


这个错误可能值得调查一下。它可以指出代码中可以修复的问题。

在我们的例子中,观察到的一个元素的大小调整引发了页面上的变化,这又导致了第一个元素的大小调整,这又触发了页面上的变化,这又导致了第一个元素的大小调整,……你知道这是如何结束的。

从本质上讲,我们创建了一个无法装入单个动画帧的无限循环。我们通过使用setTimeout()在页面上显示更改来打破它(尽管这并不完美,因为它可能会对用户造成一些闪烁)。

所以每次ResizeObserver loop limit exceeded出现在我们的哨兵现在,我们把它看作一个有用的提示,并试图找到问题的原因。

赛普拉斯的一线解决方案。编辑文件support/commands.js:

Cypress.on(
'uncaught:exception',
(err) => !err.message.includes('ResizeObserver loop limit exceeded')
);

设法解决这个问题,在React为我们的错误记录器设置。

观察者错误传播到window.onerror错误处理程序,因此通过将原始的window.onerror存储在ref中,然后可以用一个不抛出此特定错误的自定义方法替换它。其他错误被允许正常传播。

确保你在useEffect清理中重新连接了原来的onerror

const defaultOnErrorFn = useRef(window.onerror);


useEffect(() => {
window.onerror = (...args) => {
if (args[0] === 'ResizeObserver loop limit exceeded') {
return true;
} else {
defaultOnErrorFn.current && defaultOnErrorFn.current(...args);
}
};
return () => {
window.onerror = defaultOnErrorFn.current;
};
}, []);

我遇到了柏树测试无法运行的问题。 我发现,正确的方法不是处理异常,而是编辑tsconfig。以一种针对新的es6版本的方式:

. Json

{
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": "../node_modules",
"target": "es5", --> old
"target": "es6", --> new
"types": ["cypress", "@testing-library/cypress"],
"sourceMap": true
},
"include": [
"**/*.ts"
]
}