-
Notifications
You must be signed in to change notification settings - Fork 27.3k
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
Use <link rel=“prefetch”> for prefetching #5737
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,19 @@ | ||
/* global window, document */ | ||
/* global document */ | ||
import EventEmitter from 'next-server/dist/lib/EventEmitter' | ||
|
||
// smaller version of https://gist.github.com/igrigorik/a02f2359f3bc50ca7a9c | ||
function listSupports (list, token) { | ||
if (!list || !list.supports) { | ||
return false | ||
} | ||
try { | ||
return list.supports(token) | ||
} catch (e) { | ||
return false | ||
} | ||
} | ||
|
||
const supportsPrefetch = listSupports(document.createElement('link').relList, 'prefetch') | ||
const webpackModule = module | ||
|
||
export default class PageLoader { | ||
|
@@ -9,7 +22,7 @@ export default class PageLoader { | |
this.assetPrefix = assetPrefix | ||
|
||
this.pageCache = {} | ||
this.pageLoadedHandlers = {} | ||
this.prefetchCache = new Set() | ||
this.pageRegisterEvents = new EventEmitter() | ||
this.loadingRoutes = {} | ||
} | ||
|
@@ -110,10 +123,30 @@ export default class PageLoader { | |
} | ||
} | ||
|
||
async prefetch (route) { | ||
route = this.normalizeRoute(route) | ||
const scriptRoute = route === '/' ? '/index.js' : `${route}.js` | ||
if (this.prefetchCache.has(scriptRoute)) { | ||
return | ||
} | ||
this.prefetchCache.add(scriptRoute) | ||
|
||
const link = document.createElement('link') | ||
// Feature detection is used to see if prefetch is supported, else fall back to preload | ||
// Mainly this is for Safari | ||
// https://caniuse.com/#feat=link-rel-prefetch | ||
// https://caniuse.com/#feat=link-rel-preload | ||
link.rel = supportsPrefetch ? 'prefetch' : 'preload' | ||
link.href = `${this.assetPrefix}/_next/static/${encodeURIComponent(this.buildId)}/pages${scriptRoute}` | ||
link.as = 'script' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding the Unfortunately when this is fixed to use preload it will use the default priority for script. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we should be using |
||
document.head.appendChild(link) | ||
} | ||
|
||
clearCache (route) { | ||
route = this.normalizeRoute(route) | ||
delete this.pageCache[route] | ||
delete this.loadingRoutes[route] | ||
delete this.loadingRoutes[route] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @timneutkens this one looks duplicated, were you trying to delete from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I saw that while adding Malte's feedback, will be in my upcoming PR. |
||
|
||
const script = document.getElementById(`__NEXT_PAGE__${route}`) | ||
if (script) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import Link from 'next/link' | ||
|
||
export default () => { | ||
return <div> | ||
<ul> | ||
<li> | ||
<Link href='/' prefetch> | ||
<a>index</a> | ||
</Link> | ||
</li> | ||
<li> | ||
<Link href='/process-env' prefetch> | ||
<a>process env</a> | ||
</Link> | ||
</li> | ||
<li> | ||
<Link href='/counter' prefetch> | ||
<a>counter</a> | ||
</Link> | ||
</li> | ||
<li> | ||
<Link href='/about' prefetch> | ||
<a>about</a> | ||
</Link> | ||
</li> | ||
</ul> | ||
</div> | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These aren't semantically the same. Prefetch should only be used for future navigations (as in full page loads). For sub-resources fetches (like this one), preload is the right one to use.
Prefetch may appear to work for client side navigations, but may lead to double-downloads if the prefetch hasn't finished by the time the actual request is made. That is because preload hits the memory cache (which de-dupes requests) while prefetch is only de-duped once the response made it into the disk cache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm interesting, so you're saying we should use
preload
instead, but that triggers a warning if not used within 3 seconds, which is likely to cause confusion to users of Next.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, there really needs to be a "I know what I'm doing" flag to remove that warning. Very similar things happen with lazy loading resources before rendering where render-time depends on scroll.
CC @yoavweiss @igrigorik for actually authoritative advice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created this PR: #5744
Will merge it once @yoavweiss / @igrigorik get back about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed
preload
is a better choice for same page resources. On top of that, the future ofprefetch
for subresources is a bit murky.