未验证 提交 f22f88fd 编写于 作者: J James Mosier 提交者: GitHub

Always resolve after router.prefetch() (#15448)

In development or with an invalid href, `await router.prefetch()` would not resolve the promise. This PR ensures that `await router.prefetch()` always resolves, no matter if it succeeds or not.

Fixes: https://github.com/vercel/next.js/issues/15436
Relevant discussion: https://github.com/vercel/next.js/discussions/15431#discussioncomment-41264
上级 c9836676
......@@ -802,28 +802,27 @@ export default class Router implements BaseRouter {
* @param url the href of prefetched page
* @param asPath the as path of the prefetched page
*/
prefetch(
async prefetch(
url: string,
asPath: string = url,
options: PrefetchOptions = {}
): Promise<void> {
return new Promise((resolve, reject) => {
const parsed = tryParseRelativeUrl(url)
const parsed = tryParseRelativeUrl(url)
if (!parsed) return
if (!parsed) return
const { pathname } = parsed
const { pathname } = parsed
// Prefetch is not supported in development mode because it would trigger on-demand-entries
if (process.env.NODE_ENV !== 'production') {
return
}
const route = removePathTrailingSlash(pathname)
Promise.all([
this.pageLoader.prefetchData(url, asPath),
this.pageLoader[options.priority ? 'loadPage' : 'prefetch'](route),
]).then(() => resolve(), reject)
})
// Prefetch is not supported in development mode because it would trigger on-demand-entries
if (process.env.NODE_ENV !== 'production') {
return
}
const route = removePathTrailingSlash(pathname)
await Promise.all([
this.pageLoader.prefetchData(url, asPath),
this.pageLoader[options.priority ? 'loadPage' : 'prefetch'](route),
])
}
async fetchComponent(route: string): Promise<ComponentRes> {
......
......@@ -104,7 +104,7 @@ describe('Build Output', () => {
expect(parseFloat(err404FirstLoad) - 63).toBeLessThanOrEqual(0)
expect(err404FirstLoad.endsWith('kB')).toBe(true)
expect(parseFloat(sharedByAll) - 59.2).toBeLessThanOrEqual(0)
expect(parseFloat(sharedByAll) - 59.3).toBeLessThanOrEqual(0)
expect(sharedByAll.endsWith('kB')).toBe(true)
if (_appSize.endsWith('kB')) {
......
export default function AnotherPage() {
return null
}
import { useState } from 'react'
import { useRouter } from 'next/router'
export default function RouterPrefetch() {
const router = useRouter()
const [visible, setVisible] = useState(false)
const handleClick = async () => {
await router.prefetch(
process.env.NODE_ENV === 'development' ? '/another-page' : 'vercel.com'
)
setVisible(true)
}
return (
<div>
<button type="button" id="prefetch-button" onClick={handleClick}>
click
</button>
{visible && <div id="hidden-until-click">visible</div>}
</div>
)
}
/* eslint-env jest */
import { join } from 'path'
import webdriver from 'next-webdriver'
import {
findPort,
launchApp,
killApp,
nextStart,
nextBuild,
} from 'next-test-utils'
jest.setTimeout(1000 * 60 * 5)
let app
let appPort
const appDir = join(__dirname, '..')
const didResolveAfterPrefetch = async () => {
const browser = await webdriver(appPort, '/')
const text = await browser
.elementByCss('#prefetch-button')
.click()
.waitForElementByCss('#hidden-until-click')
.text()
expect(text).toBe('visible')
await browser.close()
}
describe('Router prefetch', () => {
describe('dev mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
})
afterAll(() => killApp(app))
it('should not prefetch', async () => {
const browser = await webdriver(appPort, '/')
const links = await browser
.elementByCss('#prefetch-button')
.click()
.elementsByCss('link[rel=prefetch]')
expect(links.length).toBe(0)
await browser.close()
})
it('should resolve prefetch promise', async () => {
await didResolveAfterPrefetch()
})
})
describe('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
it('should resolve prefetch promise with invalid href', async () => {
await didResolveAfterPrefetch()
})
})
})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册