為什么會存在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`;}});
});
參考
ResizeObserver