mini-css-extract-plugin icon indicating copy to clipboard operation
mini-css-extract-plugin copied to clipboard

Avoid render-blocking requests

Open evantd opened this issue 3 years ago • 8 comments

Feature Proposal

Either change the default or add an option for making CSS bundle requests non-render-blocking. This is currently possible by using a custom insert function, but an explicit option or even changing the default would be nice.

This feels like it could be risky, but I'm having trouble coming up with any concrete concerns.

Feature Use Case

Dynamically imported modules with associated CSS currently trigger warnings about render-blocking requests in Chrome's Performance Insights tool. Since webpack uses Promises to block execution of the module until the CSS chunks are loaded, the CSS chunks don't need to block any other rendering on the page.

You can see those render-blocking requests here (after DOM Content Loaded, since that's when we do the dynamic imports):

Screen Shot 2022-06-02 at 8 22 28 PM

We can prevent the requests from blocking general rendering by setting media='print' before inserting the link tag, and then switching it to media='all' once it loads. This is currently possible by using a custom insert function, like:

                      // Don't block rendering during the network fetch. Insert link tag with media=print and then only swap it to media=all once it has loaded.
                      // eslint-disable-next-line
                      insert: function (linkTag) {
                          // eslint-disable-next-line
                          var oldOnLoad = linkTag.onload;
                          linkTag.onload = function (event) {
                              linkTag.media = 'all';
                              oldOnLoad(event);
                          };
                          linkTag.media = 'print';
                          document.head.appendChild(linkTag);
                      },

Here you can see that doing so removes the render-blocking request warnings from the Performance Insights (from after DCL):

Screen Shot 2022-06-02 at 8 22 41 PM

Please paste the results of npx webpack-cli info here, and mention other relevant information

% npx webpack-cli info

  System:
    OS: Linux 5.13 Ubuntu 20.04.4 LTS (Focal Fossa)
    CPU: (8) x64 Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz
    Memory: 5.32 GB / 30.90 GB
  Binaries:
    Node: 14.19.3 - ~/.volta/tools/image/node/14.19.3/bin/node
    npm: 6.14.17 - ~/.volta/tools/image/npm/6.14.17/bin/npm
  Packages:
    html-webpack-plugin: ^5.5.0 => 5.5.0
    webpack-manifest-plugin: ^5.0.0 => 5.0.0
    webpack-node-externals: ^1.7.2 => 1.7.2
    webpack-retry-chunk-load-plugin: ^2.2.0 => 2.2.0
    webpack-stats-plugin: ^1.0.3 => 1.0.3

mini-css-extract-plugin in my project is at 1.6.2, which I realize is old, but this appears to still be relevant to the code on master.

evantd avatar Jun 03 '22 16:06 evantd

I can accept it under option, otherwise you can flash of styles

alexander-akait avatar Jun 03 '22 16:06 alexander-akait

Can you say more about how this could lead to a flash? Since the module won't be executed until after the CSS chunk's Promise resolves (and it only resolves after setting media='all'), I was thinking that a flash shouldn't be any more possible than the current way of doing things.

evantd avatar Jun 03 '22 17:06 evantd

Because this code executed only for async CSS, you can faced with a situation, when your style was loaded (but not rendered, so you got broken print styles) and you press print (it is exotic situation, but it is possible). And verse vice.

Also you can get a sutiation when you load multiple async css, so which loaded firstly (and mostly with small size) will rendered firstly and then other, it can have negative expientcy with your interfaces (different flash and layout shifting)

Also there is situation when you can use not all for link (so we should provide option/callback to setup it).

We use blocking loading by default to prevent any racing, but yes if you follow certain rules, you can get rid of these situations

alexander-akait avatar Jun 03 '22 18:06 alexander-akait

If I'm correctly understanding the use-cases you described, I think the dynamically loaded CSS would have to affect existent content (rather than content that will be added by the JavaScript part of the chunk). In that case, though, I think the same flashes would occur without my change.

Or maybe the case you're talking about is one where the chunk with the CSS is requested, and then the code that requested the chunk starts rendering content that the CSS will apply to without waiting on the Promise?

evantd avatar Jun 06 '22 17:06 evantd

I mean if you can have multiple async CSS chunks they will be rendered in race, it can have bad experience with your UI, If there were no effects, then browsers would default to non-blocking mode.

alexander-akait avatar Jun 07 '22 09:06 alexander-akait