File tree Expand file tree Collapse file tree 5 files changed +47
-1
lines changed Expand file tree Collapse file tree 5 files changed +47
-1
lines changed Original file line number Diff line number Diff line change 1+ ---
2+ ' @sveltejs/kit ' : patch
3+ ---
4+
5+ fix: yield main thread before navigating
Original file line number Diff line number Diff line change @@ -2027,7 +2027,7 @@ function _start_router() {
20272027 }
20282028
20292029 /** @param {MouseEvent } event */
2030- container . addEventListener ( 'click' , ( event ) => {
2030+ container . addEventListener ( 'click' , async ( event ) => {
20312031 // Adapted from https://github.com/visionmedia/page.js
20322032 // MIT license https://github.com/visionmedia/page.js#license
20332033 if ( event . button || event . which !== 1 ) return ;
@@ -2120,6 +2120,16 @@ function _start_router() {
21202120
21212121 event . preventDefault ( ) ;
21222122
2123+ // allow the browser to repaint before navigating —
2124+ // this prevents INP scores being penalised
2125+ await new Promise ( ( fulfil ) => {
2126+ requestAnimationFrame ( ( ) => {
2127+ setTimeout ( fulfil , 0 ) ;
2128+ } ) ;
2129+
2130+ setTimeout ( fulfil , 100 ) ; // fallback for edge case where rAF doesn't fire because e.g. tab was backgrounded
2131+ } ) ;
2132+
21232133 navigate ( {
21242134 type : 'link' ,
21252135 url,
Original file line number Diff line number Diff line change 66
77<a href =" /routing/a" >a</a >
88<a href =" /routing/ambiguous/ok.json" rel =" external" >ok</a >
9+ <a href =" /routing/next-paint" >next-paint</a >
910<a href =" /routing/symlink-from" >symlinked</a >
1011<a href ="http://localhost: {$page .url .searchParams .get (' port' )}" >elsewhere</a >
1112<a href =" /static.json" >static.json</a >
Original file line number Diff line number Diff line change 1+ <p >next-paint</p >
Original file line number Diff line number Diff line change @@ -1163,3 +1163,32 @@ test.describe('reroute', () => {
11631163 expect ( await page . textContent ( 'h1' ) ) . toContain ( 'Full Navigation' ) ;
11641164 } ) ;
11651165} ) ;
1166+
1167+ test . describe ( 'INP' , ( ) => {
1168+ test ( 'does not block next paint' , async ( { page } ) => {
1169+ // Thanks to https://publishing-project.rivendellweb.net/measuring-performance-tasks-with-playwright/#interaction-to-next-paint-inp
1170+ async function measureInteractionToPaint ( selector ) {
1171+ return page . evaluate ( async ( selector ) => {
1172+ return new Promise ( ( resolve ) => {
1173+ const startTime = performance . now ( ) ;
1174+ document . querySelector ( selector ) . click ( ) ;
1175+ requestAnimationFrame ( ( ) => {
1176+ const endTime = performance . now ( ) ;
1177+ resolve ( endTime - startTime ) ;
1178+ } ) ;
1179+ } ) ;
1180+ } , selector ) ;
1181+ }
1182+
1183+ await page . goto ( '/routing' ) ;
1184+
1185+ const client = await page . context ( ) . newCDPSession ( page ) ;
1186+ await client . send ( 'Emulation.setCPUThrottlingRate' , { rate : 100 } ) ;
1187+
1188+ const time = await measureInteractionToPaint ( 'a[href="/routing/next-paint"]' ) ;
1189+
1190+ // we may need to tweak this number, and the `rate` above,
1191+ // depending on if this proves flaky
1192+ expect ( time ) . toBeLessThan ( 400 ) ;
1193+ } ) ;
1194+ } ) ;
You can’t perform that action at this time.
0 commit comments