On android, scroll is very janky in the chat feed of a react application I am building. Upon inspecting the devtools in the performance panel I can see a significant amount of time is spent rendering
I've tried reading through a number of resources
- https://gist.github.com/paulirish/5d52fb081b3570c81e3a
- https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing
- http://jankfree.org/
- https://www.html5rocks.com/en/tutorials/speed/layers/
- https://calendar.perfplanet.com/2013/the-runtime-performance-checklist/
- https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/
- https://chromium.googlesource.com/chromium/src/+/master/docs/speed/debug-janks.md
- https://www.chromium.org/developers/how-tos/trace-event-profiling-tool/using-frameviewer
- Reducing scroll jank when using overflow-y: scroll;
- https://developers.google.com/web/fundamentals/codelabs/web-perf
And I've tried a bunch of things, none of which had a significant impact.
- I've disabled any
:hoverselectors. - No classes are changing during scroll, I've verified this with a mutation observer on the parent element with
subtree: true[0]. - There are no
mouseEnterormouseLeaveevent listeners on the elements in the list. - I'm not reading or writing
offsetHeight,scrollTopor any other values on this list during scroll. I'm usingInersectionObserverwhere applicable. contain: layouton each list item,contain: layouton the parent.- Manually removing the global react scroll event listener.
- Removed all images from being rendered in the list.
- Enabling "Layout shift regions", "Layer borders".
- Applying
will-change: transform;to the parent
The only thing I managed to do which had a significant impact was changing the fundamental layout from:
html,
body {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
overflow: hidden;
}
.scroll-parent {
height: 100vh;
overflow-y: scroll;
display: flex;
flex-direction: column-reverse;
}
to
html,
body {}
.scroll-parent {}
The latter still produced regular "Updated Layer Tree" events but they didn't add up to a significant time and more time was spent in the "Paint" section.
Enabling "Rendering > Layer borders" revealed that there were two overlapping "sets" of tiles in the former css layout.
The issue is that I need the former overflow:hidden; on the body to avoid the toolbar from auto hiding on ios safari. I also don't think that the performance should be this bad just because I have a div which has some inner scroll. AFIK scrolling should not cause the layer tree to update, it should only cause compositing? I could also resort to virtualized scrolling but I've scrolled down much longer pages with no jank so I think this would just be another workaround rather than addressing a core limitation.
Digging into the devtools I can see that the main culprit seems to be "Updated layer tree" but I'm not really sure where to go from there.
I had a go with chrome://tracing but I can't really say I know what I'm looking at
I've been working on a minimal reproducible example. This is as close as I could get https://codesandbox.io/s/test-layout-reflow-on-scroll-u1kjr?file=/src/App.tsx There are observable "Updated Layer Tree" halmarks when profiling the replica but the performance is not nearly as bad as in the real app. I put it down to the CSS complexity being much lower so the "Updated Layer Tree" runs much quicker in the replica, but I probably shouldn't be making any assumptions.
- What are some steps I can do to better profile what might be the root cause?
- Is it normal to have so many "Updated Layer Tree" events during a scroll?
[0]
observer.observe(element, { subtree: true, childList: true, attributes: true, characterData: true })
from Debugging scroll jank with chrome devtools. "Updated Layer Tree"




No comments:
Post a Comment