magento / baler

AMD module bundler and preloader for Magento 2 stores.
Open Software License 3.0
175 stars 28 forks source link

Preload tag for core-bundle.js causes delayed FMP in Chrome #20

Open DrewML opened 5 years ago

DrewML commented 5 years ago

In Chrome, a preload tag ends up pushing the priority of a resource ahead of any other, non-preloaded resource. This is detrimental to the FMP time, because CSS ends up prioritized after the preloaded JS.

Note the special treatment of core-bundle.js, getting its first byte before any other asset

Note: The timeline above shows all resources sharing bandwidth, but devtools is lying. Each resource is getting downloaded exclusively

Asset order in the DOM image

I verified through chrome://net-export/ that Chrome is signaling to the server that core-bundle.js should be the first resource sent after the initial HTML Document. Below are the streams for the first few critical assets, in the order the streams were created:

Resource Stream Weight Stream ID Parent Stream ID Exclusive Bit
Initial HTML Document 256 1 0 TRUE
core-bundle.js 220 3 0 TRUE
calendar.css 256 5 0 TRUE
styles-m.css 256 7 5 TRUE
styles-l.css 256 9 7 TRUE
require.js 220 11 3 TRUE

Looking through HTTP2_SESSION_RECV_DATA events in the net export log, you can see the real order the server flushed the responses in below. Note that Chrome used the exclusive bit on every stream, so assets are given 100% bandwidth and downloaded in the order specified between stream weight and priorities.

Real Download Order:

core-bundle.js and calendar.css are both given a parent stream ID of 0. According to the H2 spec, when the exclusive bit is used and multiple dependencies are added to a parent stream, it should create this tree

parent stream > first dependency > second dependency

Because of this, core-bundle.js gets downloaded before any of the css on the page, even though the CSS has a higher stream weight. It seems like usage of the exclusive bit on every stream causes stream weights to be ignored, based on my reading of the spec. In particular, these 2 parts:

An exclusive flag allows for the insertion of a new level of dependencies. The exclusive flag causes the stream to become the sole dependency of its parent stream, causing other dependencies to become dependent on the exclusive stream https://tools.ietf.org/html/rfc7540#section-5.3.1

Streams with the same parent SHOULD be allocated resources proportionally based on their weight https://tools.ietf.org/html/rfc7540#section-5.3.2

Since the exclusive bit on every stream means no 2 resources will share a parent, the stream weight is never used.

IMO, Chrome's handling of H2 priorities here is wrong/buggy. If Chrome knows it's going to assign a higher weight to calendar.css than to core-bundle.js, it should be making calendar.css a dependency of parent 0 before core-bundle.js, which would result in the JS being prioritized immediately after the CSS, fixing the FMP timing.

DrewML commented 5 years ago

Some discussion about this happened with Google folks on twitter

https://twitter.com/drewml/status/1177035220295413760

It's possible that this will be fixed when https://bugs.chromium.org/p/chromium/issues/detail?id=651538 is addressed. Trying to get more info on the changes