PWA Learning and Practice series articles have been compiled into gitbook-PWA Learning Handbook, and the text has been synchronized to learning-PWA-ebook. Please indicate the author and source of reprint.
This is the tenth article in the PWA Learning & Practice series. You may not have heard of or heard of Resource Hint, but in this article you’ll quickly learn about one of its page-loading performance tools. The code for this series of related demos can be found in the Github repo.
PWA, as one of the hottest technology concepts at present, has great significance for improving the security, performance and experience of Web applications, and is worth our understanding and learning. If you are interested in PWA, please pay attention to the PWA Learning and Practice series.
If you’re interested in the previous article, you can find it here:
- Start your PWA learning journey in 2018
- Learn to Use Manifest to Make your WebApp More “Native”
- Make your WebApp available offline from today
- Article 4: TroubleShooting: TroubleShooting FireBase Login Authentication Failures
- Keep in touch with Your Users: The Web Push feature
- How to Debug? Debug your PWA in Chrome
- Enhanced Interaction: Use the Notification API for reminders
- Chapter 8: Background Data Synchronization using Service Worker
- Chapter nine: Problems and solutions in PWA practice
The introduction
We know that without caching, whether it’s HTML, javascript, or some API data, every request for a page needs to be sent from the client and returned via the server. In this case, every interaction we have involving a remote request (opening a page, querying list data, dynamically loading JS scripts, and so on) will have network latency. If we can predict or specify that the page will do some network operation in advance, such as DNS resolution or preloading resources, then when we do that later, the load will be faster and the interaction will be smoother.
Of course, there are several techniques available to help with resource preloading, such as the common use of XMLHttpRequest to fetch resources and cache them. However, these technologies are application-level, not Web standards, and some requirements cannot be implemented accurately. At the same time, there are performance issues. Fortunately, there are Resource Hints, a Web standard that allows you to implement these capabilities at the browser’s native level while providing performance assurance. Let’s take a look at Resource Hint.
1. Resource Hint
Resource Hints are a set of standards that tell the browser which origin resources our Web App wants to retrieve, and which resources need to be used for future operations or browsing, so that the browser can do some pre-linking, pre-loading, etc. Resource Hints include DNS Prefetch, Preconnect, Prefetch, and Prerender.
1.1. DNS Prefetch
When we focus on front-end performance optimization, we may overlook DNS resolution. However, DNS resolution is also time-consuming. In Chrome’s Timing Breakdown Phase, the third Phase is DNS queries. DNS Prefetch helps us tell the browser that a resource from a certain source will be fetched later so that the browser (Should) parse it as soon as possible.
Resource hints are used primarily by using the Link tag. The rel attribute determines the type, and the href attribute specifies the corresponding source or resource URL. DNS Prefetch can be used like this:
<link rel="dns-prefetch" href="//yourwebsite.com">
Copy the code
1.2. Preconnect
We know that setting up a connection requires not only DNS queries, but also TCP handshakes, and in some cases TLS/SSL, which can lead to time consuming connections. So using Preconnect helps you tell the browser, “I have some resources that use a source that will help me pre-establish a connection.”
According to the spec, when you use Preconnect, the browser roughly does the following:
- First, the URL for Preconnect is parsed
- Second, the CORS is set based on the attributes in the current link element
- Set credential to true by default; If cORS is Anonymous and exists across domains, set credential to false
- And then finally connect
To use Preconnect, just set the rel property to Preconnect:
<link rel="preconnect" href="//yourwebsite.com">
Copy the code
Of course, you can also set CORS
<link rel="preconnect" href="//yourwebsite.com" crossorigin>
Copy the code
It is important to note that the standard does not specify that the browser must (but SHOULD) complete the connection process; the browser can do some of the work as it sees fit.
1.3. Prefetch
You can think of Prefetch as a resource Prefetch. In general, you can use Prefetch to specify resources that need to be used in subsequent operations or browsing, allowing the browser to fetch them ahead of time. Resources are simply fetched in advance, so the browser does not preprocess them, and resources such as CSS stylesheets and JavaScript scripts are not automatically executed and applied to the current document.
Note that unlike DNS Prefetch and Preconnect, Prefetch has an optional as attribute that specifies the type of resource to fetch. This attribute is important because different resource types have different priorities, CSPS, request priorities. The following table lists the AS attribute values for some common resources:
Resource consumer | writing |
---|---|
<audio> |
<link rel=preload as=audio href=... > |
<video> |
<link rel=preload as=video href=... > |
<track> |
<link rel=preload as=track href=... > |
<script> , Worker’s importScripts |
<link rel=preload as=script href=... > |
<link rel=stylesheet> , CSS @import |
<link rel=preload as=style href=... > |
CSS @font-face | <link rel=preload as=font href=... > |
<img> .<picture> , srcset, imageset |
<link rel=preload as=image href=... > |
SVG’s <image> , CSS *-image |
<link rel=preload as=image href=... > |
XHR, fetch | <link rel=preload as=fetch crossorigin href=... > |
Worker, SharedWorker | <link rel=preload as=worker href=... > |
<embed> |
<link rel=preload as=embed href=... > |
<object> |
<link rel=preload as=object href=... > |
<iframe> .<frame> |
<link rel=preload as=document href=... > |
HTML | <link rel=preload as=html href=... > |
It can be seen that The optional resource types of Prefetch are very rich. In addition to commonly used script and style, they even include XHR, video, img, etc., basically covering all kinds of resources in the Web. To resolve cross-domain issues with some resources in Prefetch, such as XHR, you can apply the CORS attribute to them. A basic Prefetch is also simple:
<link rel="prefetch" href="/my.little.script.js" as="script">
Copy the code
1.4. Prerender
We talked about Prefetch in the last section, but Prerender is a step up from Prefetch. This can be roughly understood as “preprocessing” (pre-execution).
Prerender resources are processed as HTML by the browser. In addition to fetching the resource, the browser MAY preprocess the resource, and other resources that the HTML page depends on, such as
Note that due to the uncontrollability of these pre-processing operations, it is recommended to use Prefetch when you only need to be able to pre-fetch a portion of the resource to speed up subsequent network requests. When using Prerender, for compatibility purposes, the target page can listen to the VisiBilityChange event and use Document.VisibilityState to determine the page state.
When prerendering a document the user agent MUST set the document’s visibilityState value to prerender. —— W3C Working Draft
Using Prerender is very simple, similar to DNS Prefetch and Preconnect, specifying the REL attribute as Prerender:
<link rel="prerender" href="//yourwebsite.com/nextpage.html">
Copy the code
2. Resource Hint specific usage
In the previous section, I focused on DNS Prefetch, Preconnect, Prefetch, and Prerender, four Resource Hint Links (RHL), and briefly explained how to use them in Link. However, you can trigger the browser’s Resource Hint in several other ways besides directly adding the corresponding link tag to the HTML. To make this more intuitive, let’s use the book search demo to see how you can use Resource Hints.
Assuming that the detail page nextpage.html and its dependency nextpage.js have been added to the demo, the jump occurs when a book in the list is clicked.
2.1. Link element in document head
This is one of the most common forms of Resource Hint, and is used in the examples we’ve covered above. For example, to specify Prefetch nextpage.js, write:
<link rel="prefetch" href="./nextpage.js" as="script">
Copy the code
2.2. HTTP Link header field
Resource hints can be used with the Link HTTP header. The Link HTTP header and the Link element are equivalent.
The Link entity-header field provides a means for serialising one or more links in HTTP headers. It is semantically equivalent to the element in HTML, as well as the atom:link feed-level element in Atom. —— RFC5988
Link consists of two parts: URI-reference and link-param. Uri-reference is equivalent to the href attribute in the link element; Link-param contains rel, title, type and other element attributes. Segmentation. Therefore, you can add the following sections to the response header:
Link: </nextpage.js>; rel="prefetch"; as="script"
Copy the code
Our demo uses the middleware koA-static, just make the following changes:
// app.js
app.use(serve(__dirname + '/public', {
maxage: 1000 * 60 * 60.setHeaders: (res, path, stats) = > {
if (/index.html/.test(path)) {
res.setHeader('Link'.'</nextpage.js>; rel="prefetch"; as="script"'); }}}));Copy the code
You’ll notice that when you access index.html, the browser asks the server for resources that nextPage.js doesn’t “need” for the page itself.
2.3. Dynamically add a link element to the document
The link element also allows us to dynamically add to the document through JS. The browser also applies the Resource Hint policy to dynamically added RHL. Link is added in the same way as normal DOM elements.
var hint = document.createElement('link');
hint.rel = 'prefetch';
hint.as = 'script';
hint.href = '/nextpage.js';
document.head.appendChild(hint);
Copy the code
2.4. Change the href attribute of the existing link element
When you change the href attribute (or the AS attribute in prefetch) of the original RHL on the page, the Resource Hint is immediately triggered for the new Resource. For example, after the following code is executed
var hint = document.querySelector('[rel="prefetch"]');
hint.href = './the.other.nextpage.js';
Copy the code
The browser receives the new Resource Hint and asks the server for the.other.nextPage.js when appropriate. Note that when you modify the AS property, the Resource Hint is also triggered.
Note that if you want to prefetch nextPage.html by modifying an existing link element, then writing as follows will trigger two requests.
var hint = document.querySelector('[rel="prefetch"]');
hint.as = 'html'; // trigger the first request, request again./nextpage.js
hint.href = './nextpage.html'; / / request/nextpage. HTML
Copy the code
2. Preload
Now that WE’ve mentioned Resource Hints, we can’t help but mention its equivalent, Preload. The browser immediately prefetches resources that require Preload and stores them in memory without affecting the page parse and load events. It is not executed until the resource’s usage tag is encountered again.
(Preload) Initiating an early fetch and separating fetching from resource execution.
For example, the following HTML fragment:
<head>
<link rel="preload" href="./nextpage.js" as="script">
<script type="text/javascript" src="./current.js"></script>
<script type="text/javascript" src="./nextpage.js"></script>
<head>
Copy the code
The browser first fetches nextPage.js, then fetches and executes current.js, and finally, when it encounters a script tag that uses the nextPage.js resource, executes the fetched nextPage.js. Since we will place script tags at the bottom of the body to ensure performance, we can consider adding preloads of these resources to the head tag to speed up page loading and rendering.
Further, we can listen for Preload and trigger custom actions
<script>
function preloadFinished(e) {... }function preloadError(e) {... }</script>
<! -- listen for load and error events -->
<link rel="preload" href="app.js" as="script" onload="preloadFinished()" onerror="preloadError()">
Copy the code
As mentioned in the introduction, in the past, if we wanted to preload some resources, we would use some application-level techniques, but we often encountered two problems:
- We need to acquire resources first and then execute when appropriate, but the two are not easily separated
- Regardless of the technical implementation, there will be some performance and experience damage
The value that Preload (including RHL like Prefetch mentioned earlier) brings to us is the separation of load and execution of resources at the browser level and the guarantee of a good performance experience at the browser level.
What is the difference between Preload and Prefetch?
This is where it is most likely to be confused with Prefetch. There’s a paragraph in the standard that explains the difference:
The application can use the preload keyword to initiate early, high-priority, and non-render-blocking fetch of a CSS resource that can then be applied by the application at appropriate time
Compared with Prefetch, Preload forces the browser to obtain resources immediately and has a higher priority (mandatory and high-priority). Therefore, you are advised to use Preload for resources that are immediately needed on the current page. On the other hand, Prefetch is optional and has a lower priority. Whether or not Prefetch is determined by the browser is applicable to Prefetch resources that may be used in the future.
In order to save unnecessary bandwidth consumption, if Preload resources are not used within 3s, Chrome Console will display a warning similar to the following image. At this point you need to think carefully about whether the resource should be preloaded.
More details about the differences between Preload And Prefetch can be found here – Preload, Prefetch And Priorities in Chrome.
3. Write at the end
This article shows you how to use Resource Hints (and Preload) to improve page loading performance and experience. Simply put:
- DNS Prefetch can help us perform DNS pre-query;
- Preconnect can help us pre-connect, for example in some redirection technologies, allowing the browser to establish a connection with the destination source earlier;
- Prefetch can help us pre-fetch the required resource (without worrying about the resource being executed). For example, we can predict the next action of the user based on the user’s behavior and then dynamically pre-fetch the required resource.
- Prerender takes things a step further by not only getting resources, but also pre-loading (executing) some resources, so if we Prerender the next page, it will feel very smooth to open it;
- Preload is like an upgraded version of Prefetch, which forces an immediate and superior fetch of resources, which is a good fit for Preload (fetch as early as possible) of some of the key render paths.
Resource Hints are not mentioned in most PWA sources, but as I mentioned in the first article
PWA itself is a collection of concepts, not a single technology, but a set of Web technologies and Web standards to optimize the security, performance, and experience of Web apps.
Resource Hint certainly fits this point.
We should not limit PWA to the common PWA contents such as Service Worker offline cache, reminders and notifications. We hope readers can also broaden their minds and understand the concepts and ideas behind PWA. In a follow-up article, therefore, I will also introduce the front-end storage (sessionStorage/localStorage/indexDB), HTTP / 2.0 and PWA progress and other related content.
In the next installment, we’ll take a look at Workbox, Google’s open source offline tool set for PWA. Workbox allows you to learn about various offline strategies and some of the issues you need to consider in a production environment. Some open source PWA solutions are also packaged based on WorkBox.
PWA Learning and Practice series
- Start your PWA learning journey in 2018
- Learn to Use Manifest to Make your WebApp More “Native”
- Make your WebApp available offline from today
- Article 4: TroubleShooting: TroubleShooting FireBase Login Authentication Failures
- Keep in touch with Your Users: The Web Push feature
- How to Debug? Debug your PWA in Chrome
- Enhanced Interaction: Use the Notification API for reminders
- Chapter 8: Background Data Synchronization using Service Worker
- Chapter nine: Problems and solutions in PWA practice
- Resource Hint – Improving page loading Performance and Experience
- Part 11: Learning offline Strategies from workbox
The resources
- Resource Hints W3C Working Draft 15 January 2018
- Preload W3C Candidate Recommendation 26 October 2017
- Preload, Prefetch And Priorities in Chrome
- Web Linking
- Page Visibility Level 2 W3C Proposed Recommendation 17 October 2017
- CORS settings attributes
- Content Security Policy Level 3 W3C Working Draft, 13 September 2016