2370
return;
2371
}
2372
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
+
2445
function pushSelfClosing(
2446
target: Array<Chunk | PrecomputedChunk>,
2447
props: Object,
3244
case 'pre': {
3245
return pushStartPreformattedElement(target, props, type);
3246
}
3247
+ case 'img': {
3248
+ return pushImg(target, props, resources);
3249
+ }
3250
// Omitted close tags
3251
case 'base':
3252
case 'area':
3254
case 'col':
3255
case 'embed':
3256
case 'hr':
3182
- case 'img':
3257
case 'keygen':
3258
case 'param':
3259
case 'source':
4316
resources.fontPreloads.forEach(flushResourceInPreamble, destination);
4317
resources.fontPreloads.clear();
4318
4319
+ resources.highImagePreloads.forEach(flushResourceInPreamble, destination);
4320
+ resources.highImagePreloads.clear();
4321
+
4322
// Flush unblocked stylesheets by precedence
4323
resources.precedences.forEach(flushAllStylesInPreamble, destination);
4324
4327
resources.scripts.forEach(flushResourceInPreamble, destination);
4328
resources.scripts.clear();
4329
4253
- resources.explicitPreloads.forEach(flushResourceInPreamble, destination);
4254
- resources.explicitPreloads.clear();
4330
+ resources.bulkPreloads.forEach(flushResourceInPreamble, destination);
4331
+ resources.bulkPreloads.clear();
4332
4333
// Write embedding preloadChunks
4334
const preloadChunks = responseState.preloadChunks;
4385
resources.fontPreloads.forEach(flushResourceLate, destination);
4386
resources.fontPreloads.clear();
4387
4388
+ resources.highImagePreloads.forEach(flushResourceInPreamble, destination);
4389
+ resources.highImagePreloads.clear();
4390
+
4391
// Preload any stylesheets. these will emit in a render instruction that follows this
4392
// but we want to kick off preloading as soon as possible
4393
resources.precedences.forEach(preloadLateStyles, destination);
4398
resources.scripts.forEach(flushResourceLate, destination);
4399
resources.scripts.clear();
4400
4321
- resources.explicitPreloads.forEach(flushResourceLate, destination);
4322
- resources.explicitPreloads.clear();
4401
+ resources.bulkPreloads.forEach(flushResourceLate, destination);
4402
+ resources.bulkPreloads.clear();
4403
4404
// Write embedding preloadChunks
4405
const preloadChunks = responseState.preloadChunks;
4939
// Flushing queues for Resource dependencies
4940
preconnects: Set<PreconnectResource>,
4941
fontPreloads: Set<PreloadResource>,
4942
+ highImagePreloads: Set<PreloadResource>,
4943
// usedImagePreloads: Set<PreloadResource>,
4944
precedences: Map<string, Set<StyleResource>>,
4945
stylePrecedences: Map<string, StyleTagResource>,
4946
bootstrapScripts: Set<PreloadResource>,
4947
scripts: Set<ScriptResource>,
4867
- explicitPreloads: Set<PreloadResource>,
4948
+ bulkPreloads: Set<PreloadResource>,
4949
4950
// Module-global-like reference for current boundary resources
4951
boundaryResources: ?BoundaryResources,
4964
// cleared on flush
4965
preconnects: new Set(),
4966
fontPreloads: new Set(),
4967
+ highImagePreloads: new Set(),
4968
// usedImagePreloads: new Set(),
4969
precedences: new Map(),
4970
stylePrecedences: new Map(),
4971
bootstrapScripts: new Set(),
4972
scripts: new Set(),
4891
- explicitPreloads: new Set(),
4973
+ bulkPreloads: new Set(),
4974
4975
// like a module global for currently rendering boundary
4976
boundaryResources: null,
5168
// both. This is to prevent identical calls with the same srcSet and sizes to be duplicated
5169
// by varying the href. this is an edge case but it is the most correct behavior.
5170
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);
5172
} else {
5173
key = getResourceKey(as, href);
5174
}
5250
}
5251
if (as === 'font') {
5252
resources.fontPreloads.add(resource);
5253
+ } else if (as === 'image' && options.fetchPriority === 'high') {
5254
+ resources.highImagePreloads.add(resource);
5255
} else {
5181
- resources.explicitPreloads.add(resource);
5256
+ resources.bulkPreloads.add(resource);
5257
}
5258
flushResources(request);
5259
}