Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<link rel="prefetch"> downloads aren't persisted as expected — use XHR to save #12800

Closed
KyleAMathews opened this issue Mar 24, 2019 · 8 comments

Comments

@KyleAMathews
Copy link
Contributor

In Gatsby v2 we shifted to using <link rel="prefetch"> to prefetch resources as it allows the browsers to be smarter about sharing network bandwidth between competing downloads as it can deprioritize our prefetching when there's higher priority API calls / image requests / etc.

One of our assumptions when we made this design decision was that resources we prefetch would well, stay prefetched. So we could prefetch a resource at the start of a session and if it was only used at the end of the session, the browser would keep it around.

I'd asked @addyosmani to review my image prefetching RFC and he mentioned prefetching can be problematic as browsers actually only keep prefetched resources around for ~5 minutes.

Which means that if a user stays on a page for 5+ minutes before clicking onto another page, the browser will need to download the next pages resources again doubling the downloaded bytes and causing extra latency when changing pages.

So we want to keep using <link rel="prefetch"> as it's the friendliest prefetch but we need to persist the prefetched resources.

One way we could do this is to use the onload event to trigger an immediate xhr which would then make the browser persist the resource e.g. <link rel="prefetch" as="image" href="http://example.com/image.png" onload="fetch('http://example.com/image.png')">

The image will be already be in the memory cache so the browser will just copy it into the more permanent cache (don't remember off top of my head the names of the different browser caches).

@KyleAMathews
Copy link
Contributor Author

KyleAMathews commented Mar 24, 2019

A working code sample:

<html>
  <head>
    <link
      rel="prefetch"
      as="image"
      href="https://images.pexels.com/photos/248797/pexels-photo-248797.jpeg"
      onload="fetch('https://images.pexels.com/photos/248797/pexels-photo-248797.jpeg')"
    />
  </head>
  <body>
    <h2>Just testing a trick</h2>
  </body>
</html>

@addyosmani
Copy link
Contributor

So the logic here is..

  • Prefetch URL with a low priority
  • When the resource is prefetched, force a fetch() which consumes the resource and persists it to the browser cache based on cache-control rules
  • Everything prefetched is now persisted under the assumption it will be needed at some point

If we can do further analysis on how well this holds up, it might work. cc @yoavweiss as a sanity check.

@yoavweiss
Copy link

A few questions:

  • Are the resources in question cacheable?
    • If so, they will (or at least should) stay in cache beyond the 5 minutes window. That window makes sure that non-cacheable (but storable) resources in cache can escape validation for the first use during that time. After that time, or in followup uses, they follow the regular revalidation rules, which means that if they are cacheable and don't require validation, they should not trigger requests even if the user lingers on the page beyond the 5 minutes.
    • Have you seen instances where these resources are being refetched if the user lingers on the prefetching page?
  • What is your use-case for prefetching images?
    • We are currently in the process of removing prefetch support for subresources, as it introduces a lot of complexity when interacting with Service Workers, as well as introduces privacy issues when considering double-keyed caches.
    • If you have strong use-cases that will justify maintaining those use-cases in some way (e.g. only for same origin, by providing markup indicating the destination SW scope, etc), it would be great to hear it.
  • I'd caution against heavily relying on prefetch's prioritization downgrade too much, as it doesn't always prevent contention with resources for the current page, especially if their discovery is delayed. It might be better to inject those prefetches only after you know that the current page is fully rendered and interactive.

@wardpeet
Copy link
Contributor

Thanks for all the information! I agree with @yoavweiss on `What is your use-case for prefetching images? unsure if it's a big deal to show op placeholder images on navigation? Also won't service worker be able to save it to cache?

@KyleAMathews
Copy link
Contributor Author

We want to prefetch images so they show up sooner :-)

When you click between pages on a Gatsby site, most of the page renders immediately but images don't as they're not discovered until the page is rendered. We want to add the ability to Gatsby to mark images as "critical" so they we can prefetch them. A good example of images loading late is this recently launched Gatsby site https://www.prima.co — try clicking between pages and you get a blinking effect when images load instead of a nice smooth single render.

This RFC I posted is related: gatsbyjs/rfcs#35

Have you seen instances where these resources are being refetched if the user lingers on the prefetching page?

I haven't seen instances of this (though I also haven't been looking) but if prefetching does follow cache-control headers then we're actually probably fine.

I'd caution against heavily relying on prefetch's prioritization downgrade too much, as it doesn't always prevent contention with resources for the current page

Hmmm that's disappointing...

It might be better to inject those prefetches only after you know that the current page is fully rendered and interactive.

Yeah we can do this — we do add some prefetches to the <head> of our rendered HTML so those would start loading immediately but we can defer those instead to start after the page is interactive.

@gatsbot
Copy link

gatsbot bot commented Apr 16, 2019

Hiya!

This issue has gone quiet. Spooky quiet. 👻

We get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here.

If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!

Thanks for being a part of the Gatsby community! 💪💜

@gatsbot gatsbot bot added the stale? Issue that may be closed soon due to the original author not responding any more. label Apr 16, 2019
@yoavweiss
Copy link

We want to prefetch images so they show up sooner :-)

When you click between pages on a Gatsby site, most of the page renders immediately but images don't as they're not discovered until the page is rendered. We want to add the ability to Gatsby to mark images as "critical" so they we can prefetch them. A good example of images loading late is this recently launched Gatsby site https://www.prima.co — try clicking between pages and you get a blinking effect when images load instead of a nice smooth single render.

So the problems with subresource prefetching are:

  • We're not sure which Service Worker scope will handle those subresources.
  • In a double-keyed cache model, those prefetches can leak resources between different origins.

Are you actually navigating between those pages? Or are those "soft navigations"?
If it's the latter, you can (dynamically) preload those images, as they are destined for the current navigation.

Otherwise, I guess we can make subresource prefetches that are destined for same origin with same SW scope work. Would require some opt-in though, so I want to make sure the use-case is there.

@gatsbot
Copy link

gatsbot bot commented May 2, 2019

Hey again!

It’s been 30 days since anything happened on this issue, so our friendly neighborhood robot (that’s me!) is going to close it.

Please keep in mind that I’m only a robot, so if I’ve closed this issue in error, I’m HUMAN_EMOTION_SORRY. Please feel free to reopen this issue or create a new one if you need anything else.

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!

Thanks again for being part of the Gatsby community!

@gatsbot gatsbot bot closed this as completed May 2, 2019
@KyleAMathews KyleAMathews added not stale and removed stale? Issue that may be closed soon due to the original author not responding any more. labels May 29, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants