nuxt / rfcs

RFCs for changes to Nuxt.js
99 stars 2 forks source link

Allow inline CSS to be cached #26

Open johnRivs opened 5 years ago

johnRivs commented 5 years ago

Note: I suggested this idea a few months ago. Since then, @manniL released a nice little package to put inlined CSS through PurgeCSS.

The thing I'm interested the most is Allow inlined CSS to be cached. The technique itself, regardless of what you're trying to inline and then cache, allows us to implement this font loading method. We would inline a character subset of the primary font variant in data URI form and then fetch the full version plus other variants. In future visits, the inlined subset would be no longer needed.


What problem does this feature solve?

Limited, not too nuanced CSS insertion options. Currently, you either inline or extract everything. I'm aware some of the following could be made into 1 or more plugins. I'm not familiar with internals to understand what's able to be implemented in the context of Nuxt.

Avoiding requests for small size CSS

When extractCSS is set to true, CSS files above certain threshold would be <link>ed as usual and the ones below it would be inlined. @manniL explains very well in his PR the problem with <link>ing the smaller files.

I'll have a CSS file per component with a few (say ~5-25) lines of CSS in it, which are even smaller than the HTTP headers of the request to retrieve the file.

Reading about critical CSS, 14kb seems to be the sweet spot, although the number could be configurable. From this article

The magic number you should care about is 14kb. That’s (give or take) how much data a server sends per round trip when the browser makes a request for a web page. You want your above-the-fold content—required styles, scripts, markup, everything—to weight 14kb or less so that the browser can start rendering it as soon as that first packet of data is received.

From Filament Group

It’s helpful to consider some basic information about how our code is transferred during page load. Every request to the remote server takes time, and each response from the server carries a limited quantity of data. In order to aim for the fastest page loading time, we want to try to fit the code required for rendering the top portion of a given page in the first response from the server, which happens to carry around 14kb of compressed code (it’s often less, but 14’s a solid goal to shoot for).

Basically, we want fit the HTML, CSS, and JavaScript that’s necessary for Start Render in that first 14kb round trip.

I have a CSS file that weighs 20-30kb in disk and is served by Nuxt as a 5.8kb file. I take it that 14kb of compressed code suggests I should look at 5.8kb when comparing to the threshold. I don't know if figuring out the size of the file after compression but before serving is possible.

Allow inlined CSS to be cached

Borrowing from Filament Group's loadCSS's approach, after all the relevant assets are downloaded, fetch the CSS files below the threshold, i.e. the ones that were inlined. Then, set something in the client side (e.g. a cookie) that will let the server know inlined CSS is no longer required. From this point on, whatever CSS was inlined would now be <link>ed, since the browser has downloaded it.

~~#### Apply some voodoo to inlined CSS Pass the inlined CSS through PurgeCSS, just like extracted files. I guess this is less PurgeCSS and more of a Webpack thing.~~

pi0 commented 5 years ago

@johnRivs Is this what you want?

Chunks:

Request 1:

Request 2:

johnRivs commented 5 years ago

Exactly.

In the first visit, CSS is inlined. When the browser is idle (or using preload, as you suggest) it would fetch that same CSS in file form and set something to tell Nuxt "Don't inline CSS and don't preload it either. Instead, simply link the assets."

Now, you mention big and small files and that's the part I don't have too clear in my head. So let's say 14kb really is the sweet spot and imagine a situation where:

a.css would be <link>ed and b.css + c.css would get inlined but we're now at 16kb combined, passing that sweet spot.

I don't know how practical this is but, would there be a way to measure how much the content weighs before responding to the browser? The idea would be to gather all the pieces of CSS relevant for the current page and go one by one adding them to the rest of the HTML. Inside the loop, when a piece of CSS makes the entire thing go above 14kb, stop it right there and <link> the remaining CSS.

This last thing sounds much more sophisticated and honestly, I don't know how much I'd trust the measurements. The inline anything below threshold and then <link> approch sounds like a good first step, although I wouldn't know what the threshold for each file should be. Maybe make that configurable with a sensible default?