In this codelab, you'll implement prefetching in two ways: with <link rel="prefetch">
and with HTTP Link
header.
The sample app is a website that has a promotional landing page with a special discount for the shop's best selling t-shirt. Since the landing page links to a single product, it's safe to assume that a high percentage of users will navigate to the product details page. This makes the product page a great candidate to prefetch on the landing page.
Measure performance
First establish the baseline performance:
- Click Remix to Edit to make the project editable.
- To preview the site, press View App. Then press Fullscreen .
- Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.
Click the Network tab.
In the Throttling drop-down list, select Fast 3G to simulate a slow connection type.
To load the product page, click Buy now in the sample app.
The product-details.html
page takes about 600 ms to load:
Prefetch the product page with <link rel="prefetch">
To improve navigation, insert a prefetch
tag in the landing page to prefetch the product-details.html
page:
- Add the following
<link>
element to the head of theviews/index.html
file:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet">
<link rel="prefetch" href="/product-details.html" as="document">
...
</head>
The as
attribute is optional but recommended; it helps the browser set the right headers and determine whether the resource is already in the cache. Example values for this attribute include: document
, script
, style
, font
, image
, and others.
To verify that prefetching is working:
- To preview the site, press View App. Then press Fullscreen .
- Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.
Click the Network tab.
In the Throttling drop-down list, select Fast 3G to simulate a slow connection type.
Clear the Disable cache checkbox.
Reload the app.
Now when the landing page loads, the product-details.html
page loads too, but at the lowest priority:
The page is kept in the HTTP cache for five minutes, after which the normal Cache-Control
rules for the document apply. In this case, product-details.html
has a cache-control
header with a value of public, max-age=0
, which means that the page is kept for a total of five minutes.
Reevaluate performance
- Reload the app.
- To load the product page, click Buy now in the sample app.
Take a look at the Network panel. There are two differences compared to the initial network trace:
- The Size column shows "prefetch cache", which means this resource was retrieved from the browser's cache rather than the network.
- The Time column shows that the time it takes for the document to load is now about 10 ms.
This is approximately a 98% reduction compared to the previous version, which took about 600 ms.
Extra credit: Use prefetch
as a progressive enhancement
Prefetching is best implemented as a progressive enhancement for the users who are browsing on fast connections. You can use the Network Information API to check the network conditions and based on that dynamically inject prefetch tags. That way, you can minimize data consumption and save costs for users on slow or expensive data plans.
To implement adaptive prefetching, first remove the <link rel="prefetch">
tag from views/index.html
:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet">
<link rel="prefetch" href="/product-details.html" as="document">
...
</head>
Then add the following code to public/script.js
to declare a function that dynamically injects the prefetch
tag when the user is on a fast connection:
function injectLinkPrefetchIn4g(url) {
if (window.navigator.connection.effectiveType === '4g') {
//generate link prefetch tag
const linkTag = document.createElement('link');
linkTag.rel = 'prefetch';
linkTag.href = url;
linkTag.as = 'document';
//inject tag in the head of the document
document.head.appendChild(linkTag);
}
}
The function works as follows:
- It checks the effectiveType property of the Network Information API to determine if the user is on a 4G (or faster) connection.
- If that condition is fulfilled, it generates a
<link>
tag withprefetch
as the type of hint, passes the URL that will be prefetched in thehref
attribute, and indicates that the resource is an HTMLdocument
in theas
attribute. - Finally, it injects the script dynamically in the
head
of the page.
Next add script.js
to views/index.html
, just before the closing </body>
tag:
<body>
...
<script src="/script.js"></script>
</body>
Requesting script.js
at the end of the page ensures that it will be loaded and executed after the page is parsed and loaded.
To make sure that the prefetching doesn't interfere with critical resources for the current page, add the following code snippet to call injectLinkPrefetchIn4g()
on the window.load
event:
<body>
...
<script src="/script.js"></script>
<script>
window.addEventListener('load', () => {
injectLinkPrefetchIn4g('/product-details.html');
});
</script>
</body>
The landing page now prefetches product-details.html
only on fast connections. To verify that:
- To preview the site, press View App. Then press Fullscreen .
- Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.
- Click the Network tab.
- In the Throttling drop-down list, select Online.
- Reload the app.
You should see product-details.html
in the Network panel:
To verify that the product page isn't prefetched on slow connections:
- In the Throttling drop-down list, select Slow 3G.
- Reload the app.
The Network panel should include only the resources for the landing page without product-details.html
:
Prefetch the stylesheet for the product page with the HTTP Link
header
The HTTP Link
header can be used to prefetch the same type of resources as the link
tag. Deciding when to use one or the other mostly depends on your preference, as the difference in performance is insignificant. In this case, you'll use it to prefetch the main CSS of the product page, to further improve its rendering.
Add an HTTP Link
header for style-product.css
in the server response for the landing page:
- Open the
server.js
file and look for theget()
handler for the root url:/
. - Add the following line at the beginning of the handler:
app.get('/', function(request, response) {
response.set('Link', '</style-product.css>; rel=prefetch');
response.sendFile(__dirname + '/views/index.html');
});
- To preview the site, press View App. Then press Fullscreen .
- Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.
- Click the Network tab.
- Reload the app.
The style-product.css
is now prefetched at the lowest priority after the landing page loads:
To navigate to the product page, click Buy now. Take a look at the Network panel:
The style-product.css
file is retrieved from the "prefetch cache" and it took only 12 ms to load.