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

[1.0] optimizations around prefetching page resources #1133

Merged
merged 29 commits into from
Jun 10, 2017
Merged

Conversation

KyleAMathews
Copy link
Contributor

Well this ended up taking a lot more time than I anticipated (I thought a day, turned out to be ~3).

But really happy with what I came up with.

This PR does two big things + has a few extras:

Fixes some bugs that had been reported around our runtime #1106 and #1101

And make our prefetching smarter. Currently we prefetch a lot of stuff early on which can be problematic as if you click on a link, the loading of that page's resource can be blocked by the ongoing prefetching of other pages. This comes up fairly often on slower connections e.g. poor mobile connections.

The goal for Gatsby is that whenever you click on a link all the (static) resources for that page are already loaded. This is easy to accomplish on a desktop with a fast network connection but how to do this on a smartphone with limited CPU & network capacity?

On a slow enough connection we can't hope to have everything downloaded before the user clicks like it's quite possible to do on a fast connection.

So ideally we'd prioritize our prefetching by what a user is more likely to click to up the odds of prefetching the right things. But how to do that? One way is to hook up analytics for a site into building so the site prefetches commonly visited pages. But that's not yet ready and in any case, won't work for many sites that don't want to bear the extra complexity.

Also we want our prefetching to be smart so that if a user does click on a link w/o prefetched resources, we can stop prefetching to let just that page resources download. So similar to the React Fiber rewrite in this sense.

I came up with a few heuristics for deciding what resources to prefetch first.

It occurred to me that our <Link> components are mounted from the top of the page down (generally). Which is significant as (again generally) you're more likely to click on links at the top of the page vs. the bottom. So we can start downloading resources for the first links to be mounted.

The next heuristic is that many pages share resources e.g. a template component. So it also counts how many times a resource is needed and prioritizes that.

So that does a decent job at deciding what resources to fetch first.

The final optimization is to make prefetching interruptible. Gatsby now limits prefetching to one resource at a time and when a page transition happens, stops prefetching until the new page is loaded. This ensures the lowest possible latency for switching to the new page.

Other optimizations

I snuck in a few other optimizations

  • Use a little mini-script loader to load scripts to avoid any render blocking 0c8abd7
  • Added a replacement to bundle-loader so we have more control over loading scripts. Basically we can now prefetch scripts but avoid evaluating them (which can be expensive and cause jank) until they're actually needed 69f60ae

@gatsbybot
Copy link
Collaborator

gatsbybot commented Jun 9, 2017

Deploy preview ready!

Built with commit fd01c58

https://deploy-preview-1133--using-drupal.netlify.com

@gatsbybot
Copy link
Collaborator

gatsbybot commented Jun 9, 2017

Deploy preview ready!

Built with commit fd01c58

https://deploy-preview-1133--gatsbygram.netlify.com

@gatsbybot
Copy link
Collaborator

gatsbybot commented Jun 9, 2017

Deploy preview ready!

Built with commit fd01c58

https://deploy-preview-1133--gatsbyjs.netlify.com

@KyleAMathews
Copy link
Contributor Author

A few more notes.

  • Gatsby now waits a second for page resources to load before transitioning to the new page. Would love to add support for adding custom per-page-type loader/skeleton components to show then at that point.
  • I created a plugin for a global progress indicator gatsby-plugin-nprogress (/cc @rstacruz). You can drop it in + customize the indicator's color in the gatsby-config.js

screen shot 2017-06-10 at 15 11 49

It seems to work quite well

Been playing with this a lot recently and it definitely seems to speed up page transitions on slow networks so quite happy about that.

@KyleAMathews KyleAMathews merged commit acbc22e into 1.0 Jun 10, 2017
@KyleAMathews KyleAMathews deleted the queue-requests branch June 10, 2017 22:34
@rstacruz
Copy link
Contributor

Very cool!

@evenstensberg
Copy link

@KyleAMathews

...the loading of that page's resource can be blocked by the ongoing prefetching of other pages.

Do you have an example of that?

@KyleAMathews
Copy link
Contributor Author

@ev1stensberg every gatsby site before this PR :-)

I'd often try loading gatsbyjs.org for example on a slow mobile connection and when I'd click a link be frustrated by the long wait before the next page loaded. The change here has helped a lot.

@evenstensberg
Copy link

Gotcha! You don't happen to have some old code samples, contra new ones?

@KyleAMathews
Copy link
Contributor Author

Hmm no but you can always clone the repo and go back in its history before this PR was merged and build from there.

@evenstensberg
Copy link

Oki! Thanks for explaining! ❤️

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

Successfully merging this pull request may close these issues.

4 participants