Use Cached SSG Data on History Navigation (#9887)
* Use Cached SSG Data on History Navigation * Add data caching test * Create a static data cache * Eliminate an if / return * Do not cache in dev mode * bump * bump * bump * bump Co-authored-by: JJ Kasper <jj@jjsweb.site>
This commit is contained in:
parent
3ece98b31b
commit
0957ed6f32
3 changed files with 72 additions and 28 deletions
|
@ -71,6 +71,8 @@ export default class Router implements BaseRouter {
|
|||
* Map of all components loaded in `Router`
|
||||
*/
|
||||
components: { [pathname: string]: RouteInfo }
|
||||
// Static Data Cache
|
||||
sdc: { [asPath: string]: object } = {}
|
||||
sub: Subscription
|
||||
clc: ComponentLoadCancel
|
||||
pageLoader: any
|
||||
|
@ -656,23 +658,31 @@ export default class Router implements BaseRouter {
|
|||
})
|
||||
}
|
||||
|
||||
_getStaticData = (asPath: string): Promise<any> => {
|
||||
_getStaticData = (asPath: string, _cachedData?: object): Promise<object> => {
|
||||
let pathname = parse(asPath).pathname
|
||||
pathname = !pathname || pathname === '/' ? '/index' : pathname
|
||||
return fetch(
|
||||
// @ts-ignore __NEXT_DATA__
|
||||
`/_next/data/${__NEXT_DATA__.buildId}${pathname}.json`
|
||||
)
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to load static props`)
|
||||
}
|
||||
return res.json()
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
;(err as any).code = 'PAGE_LOAD_ERROR'
|
||||
throw err
|
||||
})
|
||||
|
||||
return process.env.NODE_ENV === 'production' &&
|
||||
(_cachedData = this.sdc[pathname])
|
||||
? Promise.resolve(_cachedData)
|
||||
: fetch(
|
||||
// @ts-ignore __NEXT_DATA__
|
||||
`/_next/data/${__NEXT_DATA__.buildId}${pathname}.json`
|
||||
)
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to load static props`)
|
||||
}
|
||||
return res.json()
|
||||
})
|
||||
.then(data => {
|
||||
this.sdc[pathname!] = data
|
||||
return data
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
;(err as any).code = 'PAGE_LOAD_ERROR'
|
||||
throw err
|
||||
})
|
||||
}
|
||||
|
||||
getInitialProps(
|
||||
|
|
|
@ -26,7 +26,7 @@ export async function unstable_getStaticProps() {
|
|||
export default ({ world, time }) => (
|
||||
<>
|
||||
<p>hello {world}</p>
|
||||
<span>time: {time}</span>
|
||||
<span id="anotherTime">time: {time}</span>
|
||||
<Link href="/">
|
||||
<a id="home">to home</a>
|
||||
</Link>
|
||||
|
|
|
@ -110,7 +110,7 @@ const expectedManifestRoutes = () => ({
|
|||
},
|
||||
})
|
||||
|
||||
const navigateTest = () => {
|
||||
const navigateTest = (dev = false) => {
|
||||
it('should navigate between pages successfully', async () => {
|
||||
const toBuild = [
|
||||
'/',
|
||||
|
@ -133,18 +133,52 @@ const navigateTest = () => {
|
|||
await waitFor(2500)
|
||||
|
||||
// go to /another
|
||||
await browser.elementByCss('#another').click()
|
||||
await browser.waitForElementByCss('#home')
|
||||
text = await browser.elementByCss('p').text()
|
||||
expect(text).toMatch(/hello.*?world/)
|
||||
async function goFromHomeToAnother() {
|
||||
await browser.elementByCss('#another').click()
|
||||
await browser.waitForElementByCss('#home')
|
||||
text = await browser.elementByCss('p').text()
|
||||
expect(text).toMatch(/hello.*?world/)
|
||||
}
|
||||
await goFromHomeToAnother()
|
||||
|
||||
// go to /
|
||||
await browser.eval('window.didTransition = 1')
|
||||
await browser.elementByCss('#home').click()
|
||||
await browser.waitForElementByCss('#another')
|
||||
text = await browser.elementByCss('p').text()
|
||||
expect(text).toMatch(/hello.*?world/)
|
||||
expect(await browser.eval('window.didTransition')).toBe(1)
|
||||
async function goFromAnotherToHome() {
|
||||
await browser.eval('window.didTransition = 1')
|
||||
await browser.elementByCss('#home').click()
|
||||
await browser.waitForElementByCss('#another')
|
||||
text = await browser.elementByCss('p').text()
|
||||
expect(text).toMatch(/hello.*?world/)
|
||||
expect(await browser.eval('window.didTransition')).toBe(1)
|
||||
}
|
||||
await goFromAnotherToHome()
|
||||
|
||||
// Client-side SSG data caching test
|
||||
// eslint-disable-next-line no-lone-blocks
|
||||
{
|
||||
// Let revalidation period lapse
|
||||
await waitFor(2000)
|
||||
|
||||
// Trigger revalidation (visit page)
|
||||
await goFromHomeToAnother()
|
||||
const snapTime = await browser.elementByCss('#anotherTime').text()
|
||||
|
||||
// Wait for revalidation to finish
|
||||
await waitFor(2000)
|
||||
|
||||
// Re-visit page
|
||||
await goFromAnotherToHome()
|
||||
await goFromHomeToAnother()
|
||||
|
||||
const nextTime = await browser.elementByCss('#anotherTime').text()
|
||||
if (dev) {
|
||||
expect(snapTime).not.toMatch(nextTime)
|
||||
} else {
|
||||
expect(snapTime).toMatch(nextTime)
|
||||
}
|
||||
|
||||
// Reset to Home for next test
|
||||
await goFromAnotherToHome()
|
||||
}
|
||||
|
||||
// go to /something
|
||||
await browser.elementByCss('#something').click()
|
||||
|
@ -180,7 +214,7 @@ const navigateTest = () => {
|
|||
}
|
||||
|
||||
const runTests = (dev = false) => {
|
||||
navigateTest()
|
||||
navigateTest(dev)
|
||||
|
||||
it('should SSR normal page correctly', async () => {
|
||||
const html = await renderViaHTTP(appPort, '/')
|
||||
|
|
Loading…
Reference in a new issue