diff --git a/packages/next/next-server/lib/router/router.ts b/packages/next/next-server/lib/router/router.ts index 2a4380ff44..a320811a86 100644 --- a/packages/next/next-server/lib/router/router.ts +++ b/packages/next/next-server/lib/router/router.ts @@ -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 => { + _getStaticData = (asPath: string, _cachedData?: object): Promise => { 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( diff --git a/test/integration/prerender/pages/another/index.js b/test/integration/prerender/pages/another/index.js index e1a316d071..19a7709d3d 100644 --- a/test/integration/prerender/pages/another/index.js +++ b/test/integration/prerender/pages/another/index.js @@ -26,7 +26,7 @@ export async function unstable_getStaticProps() { export default ({ world, time }) => ( <>

hello {world}

- time: {time} + time: {time} to home diff --git a/test/integration/prerender/test/index.test.js b/test/integration/prerender/test/index.test.js index 9c5a803215..15406a99f5 100644 --- a/test/integration/prerender/test/index.test.js +++ b/test/integration/prerender/test/index.test.js @@ -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, '/')