bigcommerce / paper-handlebars

Paper plugin for rendering via Handlebars.js
BSD 4-Clause "Original" or "Old" License
12 stars 36 forks source link

[STRF-9950] paper-handlebars - stylesheet helper early-hints flag #187

Closed rafa-avila-bc closed 2 years ago

rafa-avila-bc commented 2 years ago

JIRA: STRF-9950

What? Why?

stylesheet helper is now able to produce resource hints.

How was it tested?

uni tests


cc @bigcommerce/storefront-team

Tiggerito commented 2 years ago

How does this generate the resource hint link tags? Or is it part of supporting early hints via CloudFlare and HTTP header parameters (which would be cool)?

If it is early hints...

Adding the stylesheet is a great first move. And it looks like the code is designed to support other resources, like the fonts CSS.

Adding the fonts CSS and fonts script in early hints could mean the main fonts are loaded early.

Supporting images would be of value. e.g. early preloading the page's main image. Would it be possible to add a helper to generate these early hints HTTP headers? Can they be placed anywhere in the theme? Being ably to add the early hint where the img is created would make things far more maintainable. e.g. where the product's main image is created, the early hint can be set. Or as an option in the responsive-img component.

Combine the above, and the resources required to render the page and reach LCP could be loaded before the HTML :-)

If it is not early hints...

Would it be beneficial to make it work with the existing helpers\resourceHints.js?

As the stylesheet is high priority and in the head, it starts to load instantly. This means any dns-prefetch or preconnect or preload also in the head probably has no benefit.

These hints have value for resources that are later discovered or are after blocking resources. e.g. a preconnect to fonts.gstatic.com has value as it is not used until the fonts CSS file has loaded. But there is no value preconnecting to fonts.googleapis.com as the fonts CSS file is requested instantly.

Also, for compatibility with Safari, the dns-prefetch and preconnect should be separated and in a specific order. e.g.

<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous"> <link rel="dns-prefetch" href="https://fonts.gstatic.com" crossorigin="anonymous">

rafa-avila-bc commented 2 years ago

@Tiggerito the changes in this PR are not related to actual link tag generation but to "metadata" that will be used in early hints generation. So:

Supporting images would be of value. e.g. early preloading the page's main image.
bookernath commented 2 years ago

Just to add on to what @rafa-avila-bc mentioned, @Tiggerito, this is us evolving our approach to resource hints to allow you to register which resources should have preload HTTP headers, which is slightly faster (even by itself) than the hints which exist in HTML markup, for obvious reasons.

As you've already guessed, this further allows us to use HTTP Early Hints when clients have support for it, and Cloudflare helps with this.

We plan to add this ability to the {{cdn}} and {{stylesheet}} helpers to begin with, but we could certainly consider other resources as well. I want to test a bit to understand at what point preloading hits the point of diminishing returns or perhaps even causes worse performance; we do have to think about things like total HTTP header size for example, or how likely it is that resources will actually make meaningful progress on their downloading during the server response time. I expect there will need to be a balance here; preloading 100 things is probably unhelpful.

Our early assumption is that getting the theme JS bundle, stylesheet, and Script Manager scripts (of certain categories) preloaded is the right starting point.

Resources like images often benefit from e.g. progressive loading of JPGs so they might benefit from preloading, or that precious time/bandwidth might be better spent somewhere else. We hope to find out through experimentation.

As a final note, a preload strategy doesn't play nicely with responsive images, because it's unclear at the time that HTTP headers are being generated what size image a client may need.

Tiggerito commented 2 years ago

This is excellent news.

From my experience in improving Largest Contentful Paint (LCP) which is Google's measure related to speed and a user's page experience. The following can make a big difference:

Early loading of the main CSS, as it blocks rendering. Early loading of the fonts CSS and theme-bundle.font.js, as it can trigger early loading of web fonts and reduce CLS issues. Early loading of the images that can be LCP candidates (home page carousel, first products in a list, products main image, logo).

It does seem that early hints do not support responsive images. So they could not be included, which would typically mean the home page carousel image. But other images tend to be based on a fixed width, so the page always loads the same version of an image.

Responsive images can typically still be preloaded. A mechanism to add preloads in the head from where the image is added would be cool. However, browsers should soon all be supporting importance="high" on the img tag, which should have the same effect and be far simpler to implement.

When those preloads are done, the main delays seem to be caused by the CPU being busy running scripts. I see improvements in LCP by actually delaying the main bundle and other CPU-intensive scripts so the CPU can get to rendering earlier. My tests have the script load early but run later.

Another improvement was related to the slider mechanism, which causes long-running scripts. I delayed it (no need to slide before the images are loaded) and split it into separate tasks per slider, so it did not stop user interaction.

I'm also seeing that the use of defer can be detrimental as it forces DCL to wait for a script to load and run. In contrast, async is not blocking like that. If a script does not need to run before the DOM is rendered, it should not be defer but async.

A similar issue comes from scripts waiting on DCL. This can often be before rendering and thus delaying it. I'm experimenting with adding a timeout to slightly delay these scripts from running, so the CPU decides to render instead.

I find only a few scripts would benefit from preloading. Basically, ones that affect the look of the page. Like Google Optimize or widget scripts.

It may be of value to have it also add link tags in order to support other browsers.