-
Notifications
You must be signed in to change notification settings - Fork 27.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update to not trigger revalidation during prefetch (#37201)
Continuation of #34498 this updates to send a `purpose: prefetch` header (related [w3c discussion](w3c/resource-hints#74)) when prefetching data routes and then on the server we skip revalidating when this header is set. When a client-transition is actually made we send a background HEAD (non-blocking) request to the data route without the `purpose: prefetch` header which signals a revalidation should occur if the data is stale. This helps alleviate the number of revalidations occurring currently caused by prefetches as a path can be prefetched but not visited during a session so may not need to be revalidated yet. Fixes: #17758 x-ref: #20521 x-ref: [slack thread](https://vercel.slack.com/archives/C031QM96T0A/p1645129445819139?thread_ts=1645124478.067049&cid=C031QM96T0A)
- Loading branch information
Showing
7 changed files
with
204 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
test/production/prerender-prefetch/app/pages/blog/[slug].js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
export default function Page(props) { | ||
return ( | ||
<> | ||
<p id="page">blog/[slug]</p> | ||
<p id="props">{JSON.stringify(props)}</p> | ||
</> | ||
) | ||
} | ||
|
||
export function getStaticProps({ params }) { | ||
console.log('revalidating /blog', params.slug) | ||
return { | ||
props: { | ||
params, | ||
now: Date.now(), | ||
}, | ||
revalidate: 2, | ||
} | ||
} | ||
|
||
export function getStaticPaths() { | ||
return { | ||
paths: ['/blog/first', '/blog/second'], | ||
fallback: false, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import Link from 'next/link' | ||
|
||
export default function Page(props) { | ||
return ( | ||
<> | ||
<p id="page">index</p> | ||
<p id="props">{JSON.stringify(props)}</p> | ||
<Link href="/blog/first"> | ||
<a id="to-blog-first">/blog/first</a> | ||
</Link> | ||
<br /> | ||
<Link href="/blog/second"> | ||
<a id="to-blog-second">/blog/second</a> | ||
</Link> | ||
<br /> | ||
</> | ||
) | ||
} | ||
|
||
export function getStaticProps() { | ||
console.log('revalidating /') | ||
return { | ||
props: { | ||
now: Date.now(), | ||
}, | ||
revalidate: 1, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { NextInstance } from 'test/lib/next-modes/base' | ||
import { createNext, FileRef } from 'e2e-utils' | ||
import { check, fetchViaHTTP, waitFor } from 'next-test-utils' | ||
import cheerio from 'cheerio' | ||
import { join } from 'path' | ||
import webdriver from 'next-webdriver' | ||
import assert from 'assert' | ||
|
||
describe('Prerender prefetch', () => { | ||
let next: NextInstance | ||
|
||
beforeAll(async () => { | ||
next = await createNext({ | ||
files: { | ||
pages: new FileRef(join(__dirname, 'app/pages')), | ||
}, | ||
dependencies: {}, | ||
}) | ||
}) | ||
afterAll(() => next.destroy()) | ||
|
||
it('should not revalidate during prefetching', async () => { | ||
const reqs = {} | ||
|
||
// get initial values | ||
for (const path of ['/blog/first', '/blog/second']) { | ||
const res = await fetchViaHTTP(next.url, path) | ||
expect(res.status).toBe(200) | ||
|
||
const $ = cheerio.load(await res.text()) | ||
const props = JSON.parse($('#props').text()) | ||
reqs[path] = props | ||
} | ||
|
||
const browser = await webdriver(next.url, '/') | ||
|
||
// wait for prefetch to occur | ||
await check(async () => { | ||
const cache = await browser.eval('JSON.stringify(window.next.router.sdc)') | ||
return cache.includes('/blog/first') && cache.includes('/blog/second') | ||
? 'success' | ||
: cache | ||
}, 'success') | ||
|
||
await waitFor(3000) | ||
await browser.refresh() | ||
|
||
// reload after revalidate period and wait for prefetch again | ||
await check(async () => { | ||
const cache = await browser.eval('JSON.stringify(window.next.router.sdc)') | ||
return cache.includes('/blog/first') && cache.includes('/blog/second') | ||
? 'success' | ||
: cache | ||
}, 'success') | ||
|
||
// ensure revalidate did not occur from prefetch | ||
for (const path of ['/blog/first', '/blog/second']) { | ||
const res = await fetchViaHTTP(next.url, path) | ||
expect(res.status).toBe(200) | ||
|
||
const $ = cheerio.load(await res.text()) | ||
const props = JSON.parse($('#props').text()) | ||
expect(props).toEqual(reqs[path]) | ||
} | ||
}) | ||
|
||
it('should trigger revalidation after navigation', async () => { | ||
const getData = () => | ||
fetchViaHTTP( | ||
next.url, | ||
`/_next/data/${next.buildId}/blog/first.json`, | ||
undefined, | ||
{ | ||
headers: { | ||
purpose: 'prefetch', | ||
}, | ||
} | ||
) | ||
const initialDataRes = await getData() | ||
const initialData = await initialDataRes.json() | ||
const browser = await webdriver(next.url, '/') | ||
|
||
await browser.elementByCss('#to-blog-first').click() | ||
|
||
await check(async () => { | ||
const data = await getData() | ||
assert.notDeepEqual(initialData, data) | ||
return 'success' | ||
}, 'success') | ||
}) | ||
}) |