@@ -2370,6 +2370,78 @@ function pushStyleContents(
23702370 return ;
23712371}
23722372
2373+ function getImagePreloadKey (
2374+ href : string ,
2375+ imageSrcSet : ?string ,
2376+ imageSizes : ?string ,
2377+ ) {
2378+ let uniquePart = '';
2379+ if ( typeof imageSrcSet === 'string' && imageSrcSet !== '' ) {
2380+ uniquePart += '[ ' + imageSrcSet + ' ] ';
2381+ if ( typeof imageSizes === 'string' ) {
2382+ uniquePart += '[ ' + imageSizes + ' ] ';
2383+ }
2384+ } else {
2385+ uniquePart += '[][]' + href ;
2386+ }
2387+ return getResourceKey ( 'image' , uniquePart ) ;
2388+ }
2389+
2390+ function pushImg (
2391+ target : Array < Chunk | PrecomputedChunk > ,
2392+ props : Object ,
2393+ resources : Resources ,
2394+ ) : null {
2395+ if (
2396+ props . loading !== 'lazy' &&
2397+ typeof props . src === 'string' &&
2398+ props . fetchPriority !== 'low'
2399+ ) {
2400+ // We have a suspensey image and ought to preload it to optimize the loading of display blocking
2401+ // resources.
2402+ const { src , imageSrcSet , imageSizes } = props ;
2403+ const key = getImagePreloadKey ( src , imageSrcSet , imageSizes ) ;
2404+ let resource = resources . preloadsMap . get ( key ) ;
2405+ if ( ! resource ) {
2406+ resource = {
2407+ type : 'preload ',
2408+ chunks : [ ] ,
2409+ state : NoState ,
2410+ props : {
2411+ rel : 'preload' ,
2412+ as : 'image' ,
2413+ // There is a bug in Safari where imageSrcSet is not respected on preload links
2414+ // so we omit the href here if we have imageSrcSet b/c safari will load the wrong image.
2415+ // This harms older browers that do not support imageSrcSet by making their preloads not work
2416+ // but this population is shrinking fast and is already small so we accept this tradeoff.
2417+ href : imageSrcSet ? undefined : src ,
2418+ imageSrcSet ,
2419+ imageSizes ,
2420+ crossOrigin : props . crossOrigin ,
2421+ integrity : props . integrity ,
2422+ type : props . type ,
2423+ fetchPriority : props . fetchPriority ,
2424+ referrerPolicy : props . referrerPolicy ,
2425+ } ,
2426+ } ;
2427+ resources . preloadsMap . set ( key , resource ) ;
2428+ if ( __DEV__ ) {
2429+ markAsRenderedResourceDEV ( resource , props ) ;
2430+ }
2431+ pushLinkImpl ( resource . chunks , resource . props ) ;
2432+ }
2433+ if (
2434+ props . fetchPriority === 'high' ||
2435+ resources . highImagePreloads . size < 10
2436+ ) {
2437+ resources . highImagePreloads . add ( resource ) ;
2438+ } else {
2439+ resources. bulkPreloads . add ( resource ) ;
2440+ }
2441+ }
2442+ return pushSelfClosing ( target , props , 'img' ) ;
2443+ }
2444+
23732445function pushSelfClosing (
23742446 target : Array < Chunk | PrecomputedChunk > ,
23752447 props : Object ,
@@ -3172,14 +3244,16 @@ export function pushStartInstance(
31723244 case 'pre ': {
31733245 return pushStartPreformattedElement ( target , props , type ) ;
31743246 }
3247+ case 'img ': {
3248+ return pushImg ( target , props , resources ) ;
3249+ }
31753250 // Omitted close tags
31763251 case 'base ':
31773252 case 'area ':
31783253 case 'br ':
31793254 case 'col ':
31803255 case 'embed ':
31813256 case 'hr ':
3182- case 'img ':
31833257 case 'keygen ':
31843258 case 'param ':
31853259 case 'source ':
@@ -4242,6 +4316,9 @@ export function writePreamble(
42424316 resources . fontPreloads . forEach ( flushResourceInPreamble , destination ) ;
42434317 resources . fontPreloads . clear ( ) ;
42444318
4319+ resources . highImagePreloads . forEach ( flushResourceInPreamble , destination ) ;
4320+ resources . highImagePreloads . clear ( ) ;
4321+
42454322 // Flush unblocked stylesheets by precedence
42464323 resources . precedences . forEach ( flushAllStylesInPreamble , destination ) ;
42474324
@@ -4250,8 +4327,8 @@ export function writePreamble(
42504327 resources . scripts . forEach ( flushResourceInPreamble , destination ) ;
42514328 resources . scripts . clear ( ) ;
42524329
4253- resources . explicitPreloads . forEach ( flushResourceInPreamble , destination ) ;
4254- resources . explicitPreloads . clear ( ) ;
4330+ resources . bulkPreloads . forEach ( flushResourceInPreamble , destination ) ;
4331+ resources . bulkPreloads . clear ( ) ;
42554332
42564333 // Write embedding preloadChunks
42574334 const preloadChunks = responseState . preloadChunks ;
@@ -4308,6 +4385,9 @@ export function writeHoistables(
43084385 resources . fontPreloads . forEach ( flushResourceLate , destination ) ;
43094386 resources . fontPreloads . clear ( ) ;
43104387
4388+ resources . highImagePreloads . forEach ( flushResourceInPreamble , destination ) ;
4389+ resources . highImagePreloads . clear ( ) ;
4390+
43114391 // Preload any stylesheets. these will emit in a render instruction that follows this
43124392 // but we want to kick off preloading as soon as possible
43134393 resources . precedences . forEach ( preloadLateStyles , destination ) ;
@@ -4318,8 +4398,8 @@ export function writeHoistables(
43184398 resources . scripts . forEach ( flushResourceLate , destination ) ;
43194399 resources . scripts . clear ( ) ;
43204400
4321- resources . explicitPreloads . forEach ( flushResourceLate , destination ) ;
4322- resources . explicitPreloads . clear ( ) ;
4401+ resources . bulkPreloads . forEach ( flushResourceLate , destination ) ;
4402+ resources . bulkPreloads . clear ( ) ;
43234403
43244404 // Write embedding preloadChunks
43254405 const preloadChunks = responseState . preloadChunks ;
@@ -4859,12 +4939,13 @@ export type Resources = {
48594939 // Flushing queues for Resource dependencies
48604940 preconnects : Set < PreconnectResource > ,
48614941 fontPreloads : Set < PreloadResource > ,
4942+ highImagePreloads : Set < PreloadResource > ,
48624943 // usedImagePreloads: Set<PreloadResource>,
48634944 precedences : Map < string , Set < StyleResource> > ,
48644945 stylePrecedences : Map < string , StyleTagResource> ,
48654946 bootstrapScripts : Set < PreloadResource > ,
48664947 scripts : Set < ScriptResource > ,
4867- explicitPreloads : Set < PreloadResource > ,
4948+ bulkPreloads : Set < PreloadResource > ,
48684949
48694950 // Module-global-like reference for current boundary resources
48704951 boundaryResources : ?BoundaryResources ,
@@ -4883,12 +4964,13 @@ export function createResources(): Resources {
48834964 // cleared on flush
48844965 preconnects : new Set ( ) ,
48854966 fontPreloads : new Set ( ) ,
4967+ highImagePreloads : new Set ( ) ,
48864968 // usedImagePreloads: new Set(),
48874969 precedences : new Map ( ) ,
48884970 stylePrecedences : new Map ( ) ,
48894971 bootstrapScripts : new Set ( ) ,
48904972 scripts : new Set ( ) ,
4891- explicitPreloads : new Set ( ) ,
4973+ bulkPreloads : new Set ( ) ,
48924974
48934975 // like a module global for currently rendering boundary
48944976 boundaryResources : null ,
@@ -5086,16 +5168,7 @@ export function preload(href: string, options: PreloadOptions) {
50865168 // both. This is to prevent identical calls with the same srcSet and sizes to be duplicated
50875169 // by varying the href. this is an edge case but it is the most correct behavior.
50885170 const { imageSrcSet, imageSizes} = options ;
5089- let uniquePart = '' ;
5090- if ( typeof imageSrcSet === 'string' && imageSrcSet !== '' ) {
5091- uniquePart += '[' + imageSrcSet + ']' ;
5092- if ( typeof imageSizes === 'string' ) {
5093- uniquePart += '[' + imageSizes + ']' ;
5094- }
5095- } else {
5096- uniquePart += '[][]' + href ;
5097- }
5098- key = getResourceKey ( as , uniquePart ) ;
5171+ key = getImagePreloadKey ( href , imageSrcSet , imageSizes ) ;
50995172 } else {
51005173 key = getResourceKey ( as , href ) ;
51015174 }
@@ -5177,8 +5250,10 @@ export function preload(href: string, options: PreloadOptions) {
51775250 }
51785251 if ( as === 'font' ) {
51795252 resources . fontPreloads . add ( resource ) ;
5253+ } else if ( as === 'image' && options . fetchPriority === 'high' ) {
5254+ resources . highImagePreloads . add ( resource ) ;
51805255 } else {
5181- resources . explicitPreloads . add ( resource ) ;
5256+ resources . bulkPreloads . add ( resource ) ;
51825257 }
51835258 flushResources ( request ) ;
51845259 }
0 commit comments