Replies: 3 comments 1 reply
-
I would like to also tie this into the wider discussion surrounding the following RFC on formalising the |
Beta Was this translation helpful? Give feedback.
-
Notes from RFC Call:
|
Beta Was this translation helpful? Give feedback.
-
I have a Layout
It works fine, but the async attribute gets removed in the output after running npm run build. I think it is the similar or same issue as described here for the CSS case. |
Beta Was this translation helpful? Give feedback.
-
Since the writing of this proposal, numerous issues have been outlined about the different options, thus I am rewriting this proposal to only include the information that is viable to implement.
What's this proposal about
When working with Web, there are numerous way to get styling (CSS) into the HTML files themselves. There are two main ways to include external style sheets into the HTML files.
The first option being the regular
<link>
usage:the other option that can be used in HTML and the CSS files themselves is the
@import
option:Using @import and why you shouldn't do it
When talking about performance, some nuances are often overlooked. One of them being using
@import
inside an HTML file, wrapped in style tags. While it seems convenient, the issue with this approach, is that it essentially removes the parallelisation of CSS file downloading.The reason for this, is because browsers implement an inert secondary parser, commonly referred to as Preload Scanner. The primary parser constructs DOM, CSSOM runs Javascript and is constantly stopping and starting as different parts of the site block it.
The Preload Scanner can - should - safely jump ahead the primary parser, and scan the rest of the document to find additional references, then begin downloading them while the primary parser is still working on the previous items. The issue here is that @import is essentially hidden from the Preload Scanner if it's after any
<link>
or<script>
tag. This causes any @imported CSS not to be downloaded in parallel, instead they will block the primary parser.To avoid any unnecessary bottlenecks, do not use @import.
Style tags in Astro
In any given
.astro
file, not one, but multiple<style>
tags can be defined. What Astro does, is that it picks up any rules inside these style tags that are present on a page, and bundles them into a single CSS file with the naming of{page}.{id}.css
. After it's done, Astro injects it into the<head>
section of the site, using a regular Link tag, or it simply pushes the CSS rules into<style>
tags into the head.This is great, and not great at the same time.
Why this is good
<link>
or<style>
The downsides
Solution
The first and most obvious solution is to utilize the parallelism of web browsers and split the CSS into multiple smaller ones - per Astro Component. This way instead of downloading one large CSS file in ~5 seconds, 5 ones could be downloaded in parallel completing the import in ~1 second.
The only case when this option would yield worse performance, if the end-user's latency to the server is incredibly large, speaking over 100-300ms. In those cases, a single larger CSS file will outperform parallelism.
For a long time now, browsers support the feature to move
<link>
tags out of the header of the page. There are upsides and downsides to this as well. The link tags also have a set ofrel=
attributes they can use if they are in the body instead of the header. Luckilyrel="stylesheet"
is one of them.When a
<link>
tag is moved from the head to the body, the render of the document starts after the header components are loaded, and the links defined in the body will continue to download and be parsed/executed meanwhile.Theoritically, instead of linking in the header, like:
it is possible - and in some cases preferred - to add the links to the body, just before the actual component.
What this does, is will let the browser load the
global.css
file, then start the rendering of the page. While theglobal.css
file is still loading, the Preload Scanner will go ahead, and in order or appearance start downloading the rest of the CSS files, in parallel. So by the time rendering reaches the next component, the CSS is already loaded and the content is properly displayed.Note: In an non-optimal case - high latency / slow network - it is possible that the rest of the CSS is not downloaded fast enough, and heavy FOUC happens. This is obviously undesired behaviour. We can get around this, as outlined in the next section.
Proposal
Since Astro components handle multiple
<style>
tags, we could give the developers the ability to manually opt-in to using the new feature, using theasync
directive on a<style>
tag, such as:This would cause the component's style to be bundled in a separate CSS file, and to be injected into the body, right before the component injection happens, turning this
.astro
example:when built, into the following HTML snippet:
Adding this optional directive gives the developers full control on what stylesheets can be loaded in the body, and risk possible FOUC, and what should be kept in the head. Since Astro supports multiple
<style>
tags, it is possible to add two of them into a component, one being a regular style, that handles the sizing of the component and it's parts, with the other being an async style, that contains elements that are not necessary or non critical from the rendering aspect of the page.This way the default behavior of Astro stays the same, so there will be no must-learn new feature, and developers can manually opt-in to using async styles inside their component.
Resources
https://csswizardry.com/2018/11/css-and-network-performance/
https://github.com/withastro/rfcs/blob/main/proposals/0008-style-script-behavior.md
Beta Was this translation helpful? Give feedback.
All reactions