fix(nuxt): delay navigation until user input is acknowledged (#27743)

This commit is contained in:
Daniel Roe 2024-06-20 23:18:57 +01:00
parent eaf5bfedf6
commit 27435a6ed0
No known key found for this signature in database
GPG Key ID: 3714AB03996F442B
4 changed files with 37 additions and 0 deletions

View File

@ -0,0 +1,19 @@
import { defineNuxtPlugin } from '../nuxt'
import { useRouter } from '../composables'
export default defineNuxtPlugin(() => {
useRouter().beforeResolve(async () => {
/**
* This gives an opportunity for the browser to repaint, acknowledging user interaction.
* It can reduce INP when navigating on prerendered routes.
*
* @see https://github.com/nuxt/nuxt/issues/26271#issuecomment-2178582037
* @see https://vercel.com/blog/demystifying-inp-new-tools-and-actionable-insights
*/
await new Promise((resolve) => {
// Ensure we always resolve, even if the animation frame never fires
setTimeout(resolve, 100)
requestAnimationFrame(() => { setTimeout(resolve, 0) })
})
})
})

View File

@ -533,6 +533,12 @@ async function initNuxt (nuxt: Nuxt) {
}
}
if (nuxt.options.experimental.navigationRepaint) {
addPlugin({
src: resolve(nuxt.options.appDir, 'plugins/navigation-repaint.client'),
})
}
nuxt.hooks.hook('builder:watch', (event, relativePath) => {
const path = resolve(nuxt.options.srcDir, relativePath)
// Local module patterns

View File

@ -43,6 +43,10 @@ describe('resolveApp', () => {
"mode": "client",
"src": "<repoRoot>/packages/nuxt/src/app/plugins/payload.client.ts",
},
{
"mode": "client",
"src": "<repoRoot>/packages/nuxt/src/app/plugins/navigation-repaint.client.ts",
},
{
"mode": "client",
"src": "<repoRoot>/packages/nuxt/src/app/plugins/check-outdated-build.client.ts",

View File

@ -513,5 +513,13 @@ export default defineUntypedSchema({
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion !== 4)
},
},
/**
* Wait for a single animation frame before navigation, which gives an opportunity
* for the browser to repaint, acknowledging user interaction.
*
* It can reduce INP when navigating on prerendered routes.
*/
navigationRepaint: true,
},
})