Preload, Observe and Intersect

Preload, Observe and Intersect

When testing your site with Lighthouse, very often if not always, amongst the many suggested parameters to fix are the critical rendering path and the offscreen images.

So, the easiest way to fix this (meaning to speed up your site) or at least to improve the perceived speed in some browsers, is to preload some of your resources with rel="preload" (most usually your CSS, light scripts, and even fonts), and to use IntersectionObserver to lazy load your images, that is if you have a lot of them on a given page.

Preload and IntersectionObserver are very new to the scene and WILL NOT work on all browsers.

Table of Contents:

  1. Preload
  2. IntersectionObserver

Preload

rel="preload" works in a way where you instruct the browser to fetch let’s say the CSS file in the exact same time with the HTML removing the obstacle it presents later when the browser starts to read and render the document (HTML). Ordinarily, when the browser gets to the CSS in the document it can’t proceed to start visually displaying the page until it downloads the CSS fully. That way CSS is an effective obstacle in the rendering path, and that’s true for any resource of the similar nature. With rel="preload" that obstacle is removed because the browser already fetches the CSS together with the HTML.

rel="preload" can be invoked in several ways:

Directly in the HTML:

<link rel="preload" href="/style.css" as="style">

In the header via PHP for example:

<?php
  header("Link: </style.css>; rel=preload; as=style; nopush", false);
  header("Link: </script.js>; rel=preload; as=script; nopush", false);
?>

In the header via the server (Nginx in this case):

add_header Link "</style.css>; rel=preload; as=style; nopush";
add_header Link "</script.js>; rel=preload; as=script; nopush";

Where nopush is an optional attribute telling the server not to initiate HTTP/2 server push which is a bit different operation than preload. With the HTTP/2 server push the server alone decides whether the resource should be prefetched to the client and the file may not be included in the client’s cache.

Using any of the directives above doesn’t mean you should omit linking the CSS file in the HTML document. Preload makes the assets downloaded and ready. You actually need to refer to them in the document as you would normally do.

Now, when using rel="preload" you’re effectively shoving up one more file in the pipe along with the HTML for the browser to swallow which now has initially two files to fetch simultaneously when it lands on a page (CSS and HTML). So, it’s a trade off, meaning you shouldn’t preload too many or/and massive files because that will have a negative impact. The page will in effect load more slowly than having those assets blocking the rendering path in the first place. That’s why you should test everything.

External CSS files (and other files such as scripts, etc.) are cached locally upon the first use and from there no longer downloaded from the server, thus they do not hinder the rendering path anymore, except for the first time visitors. So, they present a “problem” ONLY upon the first use. Since preload function with nopush directive will pull the file (if there’s one) from the client’s cache, you can use these headers for all the visitors (unique and repeating).

Conceptually, a preloaded response ought to be committed to the HTTP cache, as it is initiated by the client, and also be available in the memory cache and be re-usable at least once within the lifetime of a fetch group. - w3.org.

Conclusion: Test whether the first time visitor is better off with rendering path blocked or you should preload some of the resources. Test whether you should, and what to preload.

IntersectionObserver

In the “old” days there were many solutions for lazy loading the images but all of them were bloated by the nature of their very design since you we’re needed to deploy massive chunks of scripts in order to achieve the effect, and the effect was mostly visual rather then boosting the performance.

Now with the IntersectionObserver the heavy lifting is left to the browser itself. Basically this neat function is built-in in the newest browsers and all you have to do is provide a small script in order to employ this feature. There are many suggested solutons across the web but I would suggest lozad.js.

All you need to do is to include this script in the HTML. You can download the script and serve it from your server or from a CDN of your choice, or even inline it.

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/lozad/dist/lozad.min.js"></script>

Then, provide a class="lozad" to your images and equal their URLs to data-src instead of src. It should look like this:

<img class="lozad" data-src="image.png" />

Finally initiate the lozad script. There are several variants for initiating depending of what you want to achieve.

<script>
const observer = lozad();
observer.observe();
</script>

The only drawback here is that by using data-src instead of src, when it comes to your images, you’re effectively hiding them from the web crawlers thus probably making them unindexable which is bad for searh engine optimization (SEO), that is if you’re dependant on an image search traffic.

So the best strategy here is to serve your above the fold images regularly, with src and without the help of lozad.js, and the rest with data-src and with lozad.js, plus you can reveal all of your images to the crawler bots via the image schema mark-up:

<meta itemprop="image" content="image.jpg">

Wrap up

Don’t use rel="preload" indiscriminately. Test and see where it actually increases the performances for the first time visitors. Definitely use IntersectionObserver to lazy load your images if you’re heavy on the media.

Powered by Hugo, hosted on AWS. © 2020. Some rights reserved.