为什么会存在ResizeObserver错误
ResizeObserver loop completed with undelivered notifications.
ResizeObserver用于监听元素content size和border size的变化。但是元素的变化和监听可能会导致循环触发,例如有元素A,监听元素A尺寸变化后将元素A的宽度加10px,如果没有特殊处理,那么线程将一直被占用无法进行其他任务,因为:A 宽度加10,监听到元素A尺寸变化后,A 宽度加10,监听到元素A尺寸变化后,A 宽度加10…就这么一直下去。
所以浏览器引入了策略,触发ResizeObserver事件后,所有回调会在这一帧绘制到屏幕前执行,在遍历执行所有回调函数时对于触发回调的目标元素和其父元素的ResizeObserver的监听回调会推迟到下一帧执行,同时会在控制台抛出错误ResizeObserver loop completed with undelivered notifications.
也就是说:
A > B 当A宽度增加时,B宽度同步增加同时监听A元素和B元素的变化。那么当A元素变化时监听B元素尺寸变化的回调会同步执行,并且A元素变化的监听函数会推迟到下一帧执行,并且控制台报错,因为有未执行的回调。
Contaienr > A > B 当A宽度增加时,B宽度同步增加同时监听A元素和B元素的变化,Container元素的宽度同样会增加。那么当A元素变化时监听B元素尺寸变化的回调会同步执行,并且A元素和Container元素变化的监听函数会推迟到下一帧执行并且控制台报错,因为有未执行的回调。
怎么避免
避免循环执行:在回调中判断达到目标值之后就不修改元素尺寸,导致回调意外触发。
延迟尺寸修改到下一帧:将执行顺序改为,帧1,A元素尺寸变化 -> 帧2,执行A元素尺寸变化的回调 -> 帧3,元素尺寸变化 -> … 这样一直下去,就不会触发控制台报错,因为监听和触发是分帧进行的。
const resizeObserver = new ResizeObserver((entries) => {
requestAnimationFrame(() => {
for (const entry of entries) {
entry.target.style.width = `${entry.contentBoxSize[0].inlineSize + 10}px`;
}
});
});