Quantcast
Channel: Updates
Viewing all articles
Browse latest Browse all 599

Get Ready for Priority Hints

$
0
0

Get Ready for Priority Hints

As performance becomes increasingly important, it's exciting to see browsers implement new features which give developers more control over resource loading. Resource Hints such as rel=preload and rel=preconnect give developers more control over resource loading and connections to cross-origin servers, respectively. Client Hints expose details of a user's device and preferences that developers can use to improve performance in nuanced ways. Continuing in this vein, a new experimental feature known as Priority Hints is available through an Origin Trial in Chrome Beta which will allow you to tell the browser how resources should be prioritized.

Resource priority? What's that?

When a browser downloads a resource, the resource is assigned a priority. By default, priorities depend on the type of resource (e.g., script, image, etc.), and the location of the resource reference in the document. For example in Chrome, CSS loaded in typical fashion via the <link> element in the <head> will be assigned a priority of highest, as it blocks rendering. Images in the viewport may be assigned a priority of high, whereas images outside the viewport may be assigned a priority of low. A <script> loaded at the end of the document may receive a priority assignment of medium or low, but this can be influenced by defer and async.

Resources and their priorities

Figure 1. A list of resources and their corresponding priorities in the network panel of DevTools.

For a long time, we've had little control over resource priority beyond modifying the critical rendering path until rel=preload came around. rel=preload changes the discovery order of a resource by telling the browser about it before the browser would otherwise find it in due course. At the same time, rel=preload doesn't reprioritize the resource, but only sets it the default priority for that particular resource type. Regardless, there are times when browsers prioritize resources in undesirable ways in specific situations: async scripts may be assumed to be of low priority when that may not have been the author's intent, images may be of higher priority than, e.g., non-critical stylesheets, etc. These are the kind of situations Priority Hints can help developers address.

Using priority hints

Priority Hints can be set for resources in HTML by specifying an importance attribute on a <script>, <img>, or <link> element (though other elements such as <iframe> may see support later). An example can be something like this:

<!-- An image the browser assigns "High" priority, but we don't actually want that. -->
<img src="/images/in_viewport_but_not_important.svg" importance="low" alt="I'm an unimportant image!">

The importance attribute accepts one of three values:

  • high: The resource may be prioritized, if the browser's own heuristics don't prevent that from happening.
  • low: The resource may be _de_prioritized, if the browser's heuristics permit.
  • auto: Let the browser decide what priority is appropriate for a resource. This is the default value.

Because <link> elements are affected by the importance attribute, this means priority can be changed not only for typical stylesheet includes, but also for rel=preload hints:

<!-- We want to initiate an early fetch for a resource, but also deprioritize it -->
<link rel="preload" href="/js/script.js" as="script" importance="low">

Priority Hints aren't restricted to HTML usage. You can also change the priority of fetch requests via the importance option, which takes the same values as the HTML attribute:

fetch("https://example.com/", {importance: "low"}).then(data => {
    // Do whatever you normally would with fetch data
});

Priority Hints have a slightly different impact depending on your network stack. With HTTP/1.X, the only way for the browser to prioritize resources is to delay their requests from going out. As a result, lower priority requests simply hit the network after high priority ones, assuming that there are higher priority requests in the queue. If there aren't, the browser may still delay some low priority requests if it predicts that higher priority requests will come along soon (e.g., if the document's <head> is still open and rendering critical resources are likely to be discovered there.

With HTTP/2, the browser may still delay some low-priority requests, but on top of that, it can also set their resource's stream priority to a lower level, enabling the server to better prioritize the resources it is sending down.


Note: Priority Hints in its current form does not affect <iframe> elements, but may, as the implementation matures. This could be useful for demoting priority of third party <iframe>s and their subresources.


So in what circumstances might Priority Hints come in useful? Let's take a look at some quick use cases and find out!

How can I tell if Priority Hints works?

The easiest way to tell if Priority Hints are working is to load your site, open the network panel in DevTools, and ensure the Priority column is checked by right clicking on any of the column headers and potentially enabling it.

DevTools network panel header context menu

Figure 2. The header options context menu in the network panel of DevTools with the Priority option highlighted.

Once enabled, the priority information for resources will be visible as shown in Figure 1. From here, pick any resource in the list and look at its priority. For example, I've chosen a script assigned a low priority in the browser:

Low priority script resource

Figure 3. A script element listed in DevTools given a low priority.

This script is requested via a <script> tag in the footer and uses the defer attribute as well, which causes the browser to lower this script's priority. Let's change that and give it an importance attribute with a value of high:

<script src="/js/app.js" defer importance="high"></script>

When this change is made and deployed, I reload the page and check the value of the Priority column for the script, which should now be given a higher priority:

High priority script resource

Figure 4. A script element listed in DevTools given a high priority.

That's pretty much how it works: If you drop a hint that you would like an element to be prioritized differently, check that resource's priority value in DevTools. If it changes, your priority hint did something!

Use cases

Resource priorities are nuanced and fluctuate based on a number of factors determined by the browser. Once you modify them, the effect can start to become a little less clear. Let's take a look at a few cases where Priority Hints can improve performance.

Deprioritizing images

Browsers do their best to assign reasonable priorities for images so that those in the viewport appear as soon as reasonably possible. In most cases, that's what you want them to do, but what if some above the fold imagery just isn't as important as other page resources? Priority Hints may provide a solution for that.

Here's a common scenario: A carousel of images is at the top of a page with the first slide visible and the remaining slides invisible. The markup of this carousel might look something like this:

<ul class="carousel">
    <!-- This item is visible, since it's the first. -->
    <li class="carousel__item"><img src="img/carousel-1.jpg" alt="I'm a carousel image!"></li>
    <!-- The next few, not so much, as they are hidden by CSS, or occluded by other elements. -->
    <li class="carousel__item"><img src="img/carousel-2.jpg" alt="I'm a carousel image!"></li>
    <li class="carousel__item"><img src="img/carousel-3.jpg" alt="I'm a carousel image!"></li>
    <li class="carousel__item"><img src="img/carousel-4.jpg" alt="I'm a carousel image!"></li>
</ul>

Because of browser heuristics, all four images may be given a high priority ranking, even though three of them are not initially visible. The browser can't really know when those image will actually be scrolled into view, so the cautious thing to do here is to consider them "in the viewport". At the same time, that may not be the desired outcome from the developer's perspective, as they know that those images are of lower priority than the async script that is responsible for making the carousel interactive in the first place.

They could use rel=preload to preload the first image in the carousel, but doing so may not provide the outcome we expect: Using rel=preload may effectively prioritize that image above everything else, and if that image is large, it may block rendering as it will get downloaded before critical stylesheets or blocking scripts. Priority Hints may be the solution here:

<ul class="carousel">
    <!-- We'll let the browser know this image is important: -->
    <li class="carousel__item"><img src="img/carousel-1.jpg" alt="I'm a carousel image!" importance="high"></li>
    <!-- But we'll set the less-important ones to low priority: -->
    <li class="carousel__item"><img src="img/carousel-2.jpg" alt="I'm a carousel image!" importance="low"></li>
    <li class="carousel__item"><img src="img/carousel-3.jpg" alt="I'm a carousel image!" importance="low"></li>
    <li class="carousel__item"><img src="img/carousel-4.jpg" alt="I'm a carousel image!" importance="low"></li>
</ul>

When we assign the off-screen images low priority, this will create less contention between the remaining high priority images and other high priority resources.

Re-prioritizing scripts

The priority of script resource downloads varies wildly in Chrome depending on the script tag's location in the HTML, and on whether the script is declared as async or defer. That means that as a developer, when you avoid making your script a blocking one (which is a known best-practice), you're also implicitly telling the browser that your script is not that important.

While those heuristics work well for many common cases, they may not work well for you.

Maybe you're trying to load a critical script, but in a non-blocking way, so you've made it async to make sure it runs whenever it is available. One example for that may be a script that's responsible for parts of the page's interaction, but which shouldn't block rendering.

Alternatively, maybe you have a blocking script at the bottom of the page (as it relies on running in a specific DOM state), but at the same time, it should not necessarily run before other async scripts, and therefore can be deprioritized.

There exist various hacks that enable you to work around some of these heuristics, but Priority Hints enable you to explicitly declare your intention to the browser and have it do the right thing.

So, if you wanted to prioritize an async script, you could indicate:

<script src="async_but_important.js" async importance="high"></script>

Similarly, for a bottom-of-the-page blocking script, you could indicate the fact that it's less important than other resources, by stating it explicitly:

<script src="blocking_but_unimportant.js" importance="low"></script> 

Deprioritizing fetches

This may not be a common scenario, but it can happen in modern applications: Let's say you have a high volume of fetch calls that fire around the same time. Because fetches are given high priority, they'll contend with one another (and other high priority requests) if enough of them occur in the same space of time. What you could do in this scenario is set an importance of low on fetches for non-critical data:

// Important user data (high by default)
let userData = await fetch("/user");

// Less important content data (explicitly low)
let newsFeedContent = await fetch("/content/news-feed", {importance: "low"});
let suggestedContent = await fetch("/content/suggested", {importance: "low"});

This approach ensures that fetches for critical data won't contend with other fetches for less important data. This could potentially improve performance in some scenarios, particularly where bandwidth is low, and the number of fetch calls is high.

Caveats and conclusion

Now that you've gotten a taste, and you're ready to run out there and start using Priority Hints: hold on! There's a few things you should be aware of before you start dropping hints all over the place.

Priority Hints are hints, not instructions

Hint is the key word. When it comes to resource prioritization, the browser has the final say. Sure, you can slap them on a bunch of elements, and the browser may do what you're asking it to. Or it may ignore some hints and decide the default priority is the best choice for the given situation. This behavior may change as Chrome's implementation matures, so test often!

It's going to take trial and error

Perhaps because Priority Hints are hints rather than instructions, it will take some trial and error to observe its effects. One useful way of looking at how Priority Hints work is to compare them to rel=preload: Where rel=preload's effects are often observable and easily measurable, Priority Hints are much more nuanced. If you don't notice any difference when using them, it could be for any number of reasons, including, but not limited to:

  1. Resource priorities help to make sure critical resources get to the browser before non-critical ones. But that only helps in environments where resource download is a bottleneck. That happens when you're using HTTP/1.X, where the number of connections the browser has open is limiting the amount of resources you can download for each round-trip-time. This also happens when using HTTP/2, but mainly in bandwidth constrained environments. High bandwidth HTTP/2 connections are less likely to benefit from better resource prioritization.
  2. HTTP/2 servers and their prioritization implementations are… not always perfect. Pat Meenan wrote about common hurdles in such implementations and how to fix them. Andy Davies has run a few tests to see which CDNs and services are getting it right. But generally, if you see that HTTP/2 prioritization is not having the impact you expect it to have, make sure that your server is handling it right.
  3. The browser either ignored the hint you gave it, or you attempted to set a priority for a resource that would have been the same as the browser's original choice.

A good way to approach using Priority Hints is that it's a fine-tuning optimization technique that should come later in your performance improvement plan rather than sooner. If you haven't looked at other techniques like image optimization, code splitting, rel=preload, and so forth, do those things first and consider Priority Hints later.

Priority Hints are experimental

The Priority Hints implementation is, like your favorite website from 1996: under construction. The API shape and functionality is not yet set in stone. Given this reality, you need to be aware that the behavior of Priority Hints and their impact could change over time. If you plan to experiment with them, you probably want to keep track of the feature and its implementation evolution. At the same time, as Priority Hints is a performance optimization, those modifications should not cause breaking changes, but may render what you're trying to use Priority Hints for less effective.

Try them out!

Starting from Chrome 73, Priority Hints are going to an Origin Trial. That means that you can register your domain and have the feature turned on for your users for the next two releases of Chrome.

We would love you to take the feature out for a spin, try it to improve your site's performance, and report back the results. We want to get a better understanding of the real world benefits of shipping what we have now, despite the caveats mentioned above, before potentially iterating over the feature a bit more.

So please, if you love speeding up websites and want to try to make them faster while helping us improve the feature, take Priority Hints out for a spin, and let us know how it went!


Viewing all articles
Browse latest Browse all 599

Trending Articles