Skip to content

Commit 9edf470

Browse files
authored
[Fizz] declare bootstrap script preloads to be fetchPriority: 'low' (#27189)
Generally scripts should not be preloaded before images but if they arrive earlier than image preloads (or images) the network (or server) may be saturated responding to inflight script preloads and not sufficiently prioritize images arriving later. This change marks the preloaded bootstrap script with a `low` fetch priority to signal to supporting browsers that the request should be deprioritized. This should make the preload operate similar to async script fetch priority which is low by default according to https://web.dev/fetch-priority/ Additionally the bootstrap script preloads will emit before preinitialized scripts do. Normal script preloads will continue to be prioritized after stylesheets This change can land separatrely but is part of a larger effort to implement elevating image loading and making script loading less blocking. Later changes will emit used suspensey images earlier in the queue and will stop favoring scripts over images that are explicitly preloaded
1 parent ea17cc1 commit 9edf470

File tree

7 files changed

+83
-24
lines changed

7 files changed

+83
-24
lines changed

packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4245,6 +4245,8 @@ export function writePreamble(
42454245
// Flush unblocked stylesheets by precedence
42464246
resources.precedences.forEach(flushAllStylesInPreamble, destination);
42474247

4248+
resources.bootstrapScripts.forEach(flushResourceInPreamble, destination);
4249+
42484250
resources.scripts.forEach(flushResourceInPreamble, destination);
42494251
resources.scripts.clear();
42504252

@@ -4322,6 +4324,9 @@ export function writeHoistables(
43224324
// but we want to kick off preloading as soon as possible
43234325
resources.precedences.forEach(preloadLateStyles, destination);
43244326

4327+
// bootstrap scripts should flush above script priority but these can only flush in the preamble
4328+
// so we elide the code here for performance
4329+
43254330
resources.scripts.forEach(flushResourceLate, destination);
43264331
resources.scripts.clear();
43274332

@@ -4875,6 +4880,7 @@ export type Resources = {
48754880
// usedImagePreloads: Set<PreloadResource>,
48764881
precedences: Map<string, Set<StyleResource>>,
48774882
stylePrecedences: Map<string, StyleTagResource>,
4883+
bootstrapScripts: Set<PreloadResource>,
48784884
scripts: Set<ScriptResource>,
48794885
explicitStylesheetPreloads: Set<PreloadResource>,
48804886
// explicitImagePreloads: Set<PreloadResource>,
@@ -4901,6 +4907,7 @@ export function createResources(): Resources {
49014907
// usedImagePreloads: new Set(),
49024908
precedences: new Map(),
49034909
stylePrecedences: new Map(),
4910+
bootstrapScripts: new Set(),
49044911
scripts: new Set(),
49054912
explicitStylesheetPreloads: new Set(),
49064913
// explicitImagePreloads: new Set(),
@@ -5470,6 +5477,7 @@ function preloadBootstrapScript(
54705477
rel: 'preload',
54715478
href: src,
54725479
as: 'script',
5480+
fetchPriority: 'low',
54735481
nonce,
54745482
integrity,
54755483
crossOrigin,
@@ -5481,7 +5489,7 @@ function preloadBootstrapScript(
54815489
props,
54825490
};
54835491
resources.preloadsMap.set(key, resource);
5484-
resources.explicitScriptPreloads.add(resource);
5492+
resources.bootstrapScripts.add(resource);
54855493
pushLinkImpl(resource.chunks, props);
54865494
}
54875495

@@ -5511,6 +5519,7 @@ function preloadBootstrapModule(
55115519
const props: PreloadModuleProps = {
55125520
rel: 'modulepreload',
55135521
href: src,
5522+
fetchPriority: 'low',
55145523
nonce,
55155524
integrity,
55165525
crossOrigin,
@@ -5522,7 +5531,7 @@ function preloadBootstrapModule(
55225531
props,
55235532
};
55245533
resources.preloadsMap.set(key, resource);
5525-
resources.explicitScriptPreloads.add(resource);
5534+
resources.bootstrapScripts.add(resource);
55265535
pushLinkImpl(resource.chunks, props);
55275536
return;
55285537
}

packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -610,17 +610,30 @@ describe('ReactDOMFizzServer', () => {
610610
});
611611

612612
expect(getVisibleChildren(container)).toEqual([
613-
<link rel="preload" href="init.js" as="script" nonce={CSPnonce} />,
614613
<link
615614
rel="preload"
615+
fetchpriority="low"
616+
href="init.js"
617+
as="script"
618+
nonce={CSPnonce}
619+
/>,
620+
<link
621+
rel="preload"
622+
fetchpriority="low"
616623
href="init2.js"
617624
as="script"
618625
nonce={CSPnonce}
619626
integrity="init2hash"
620627
/>,
621-
<link rel="modulepreload" href="init.mjs" nonce={CSPnonce} />,
622628
<link
623629
rel="modulepreload"
630+
fetchpriority="low"
631+
href="init.mjs"
632+
nonce={CSPnonce}
633+
/>,
634+
<link
635+
rel="modulepreload"
636+
fetchpriority="low"
624637
href="init2.mjs"
625638
nonce={CSPnonce}
626639
integrity="init2hash"
@@ -640,17 +653,30 @@ describe('ReactDOMFizzServer', () => {
640653
resolve({default: Text});
641654
});
642655
expect(getVisibleChildren(container)).toEqual([
643-
<link rel="preload" href="init.js" as="script" nonce={CSPnonce} />,
644656
<link
645657
rel="preload"
658+
fetchpriority="low"
659+
href="init.js"
660+
as="script"
661+
nonce={CSPnonce}
662+
/>,
663+
<link
664+
rel="preload"
665+
fetchpriority="low"
646666
href="init2.js"
647667
as="script"
648668
nonce={CSPnonce}
649669
integrity="init2hash"
650670
/>,
651-
<link rel="modulepreload" href="init.mjs" nonce={CSPnonce} />,
652671
<link
653672
rel="modulepreload"
673+
fetchpriority="low"
674+
href="init.mjs"
675+
nonce={CSPnonce}
676+
/>,
677+
<link
678+
rel="modulepreload"
679+
fetchpriority="low"
654680
href="init2.mjs"
655681
nonce={CSPnonce}
656682
integrity="init2hash"
@@ -3797,12 +3823,23 @@ describe('ReactDOMFizzServer', () => {
37973823
expect(getVisibleChildren(document)).toEqual(
37983824
<html>
37993825
<head>
3800-
<link rel="preload" href="foo" as="script" />
3801-
<link rel="preload" href="bar" as="script" />
3802-
<link rel="preload" href="baz" as="script" integrity="qux" />
3803-
<link rel="modulepreload" href="quux" />
3804-
<link rel="modulepreload" href="corge" />
3805-
<link rel="modulepreload" href="grault" integrity="garply" />
3826+
<link rel="preload" fetchpriority="low" href="foo" as="script" />
3827+
<link rel="preload" fetchpriority="low" href="bar" as="script" />
3828+
<link
3829+
rel="preload"
3830+
fetchpriority="low"
3831+
href="baz"
3832+
as="script"
3833+
integrity="qux"
3834+
/>
3835+
<link rel="modulepreload" fetchpriority="low" href="quux" />
3836+
<link rel="modulepreload" fetchpriority="low" href="corge" />
3837+
<link
3838+
rel="modulepreload"
3839+
fetchpriority="low"
3840+
href="grault"
3841+
integrity="garply"
3842+
/>
38063843
</head>
38073844
<body>
38083845
<div>hello world</div>
@@ -3866,14 +3903,27 @@ describe('ReactDOMFizzServer', () => {
38663903
expect(getVisibleChildren(document)).toEqual(
38673904
<html>
38683905
<head>
3869-
<link rel="preload" href="foo" as="script" />
3870-
<link rel="preload" href="bar" as="script" />
3871-
<link rel="preload" href="baz" as="script" crossorigin="" />
3872-
<link rel="preload" href="qux" as="script" crossorigin="" />
3873-
<link rel="modulepreload" href="quux" />
3874-
<link rel="modulepreload" href="corge" />
3906+
<link rel="preload" fetchpriority="low" href="foo" as="script" />
3907+
<link rel="preload" fetchpriority="low" href="bar" as="script" />
3908+
<link
3909+
rel="preload"
3910+
fetchpriority="low"
3911+
href="baz"
3912+
as="script"
3913+
crossorigin=""
3914+
/>
3915+
<link
3916+
rel="preload"
3917+
fetchpriority="low"
3918+
href="qux"
3919+
as="script"
3920+
crossorigin=""
3921+
/>
3922+
<link rel="modulepreload" fetchpriority="low" href="quux" />
3923+
<link rel="modulepreload" fetchpriority="low" href="corge" />
38753924
<link
38763925
rel="modulepreload"
3926+
fetchpriority="low"
38773927
href="grault"
38783928
crossorigin="use-credentials"
38793929
/>

packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ describe('ReactDOMFizzServerBrowser', () => {
8484
);
8585
const result = await readResult(stream);
8686
expect(result).toMatchInlineSnapshot(
87-
`"<link rel="preload" href="init.js" as="script"/><link rel="modulepreload" href="init.mjs"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
87+
`"<link rel="preload" href="init.js" as="script" fetchPriority="low"/><link rel="modulepreload" href="init.mjs" fetchPriority="low"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
8888
);
8989
});
9090

@@ -500,7 +500,7 @@ describe('ReactDOMFizzServerBrowser', () => {
500500
);
501501
const result = await readResult(stream);
502502
expect(result).toMatchInlineSnapshot(
503-
`"<link rel="preload" href="init.js" as="script" nonce="R4nd0m"/><link rel="modulepreload" href="init.mjs" nonce="R4nd0m"/><div>hello world</div><script nonce="${nonce}">INIT();</script><script src="init.js" nonce="${nonce}" async=""></script><script type="module" src="init.mjs" nonce="${nonce}" async=""></script>"`,
503+
`"<link rel="preload" href="init.js" as="script" fetchPriority="low" nonce="R4nd0m"/><link rel="modulepreload" href="init.mjs" fetchPriority="low" nonce="R4nd0m"/><div>hello world</div><script nonce="${nonce}">INIT();</script><script src="init.js" nonce="${nonce}" async=""></script><script type="module" src="init.mjs" nonce="${nonce}" async=""></script>"`,
504504
);
505505
});
506506
});

packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe('ReactDOMFizzServerNode', () => {
9898
pipe(writable);
9999
jest.runAllTimers();
100100
expect(output.result).toMatchInlineSnapshot(
101-
`"<link rel="preload" href="init.js" as="script"/><link rel="modulepreload" href="init.mjs"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
101+
`"<link rel="preload" href="init.js" as="script" fetchPriority="low"/><link rel="modulepreload" href="init.mjs" fetchPriority="low"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
102102
);
103103
});
104104

packages/react-dom/src/__tests__/ReactDOMFizzStaticBrowser-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ describe('ReactDOMFizzStaticBrowser', () => {
8484
});
8585
const prelude = await readContent(result.prelude);
8686
expect(prelude).toMatchInlineSnapshot(
87-
`"<link rel="preload" href="init.js" as="script"/><link rel="modulepreload" href="init.mjs"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
87+
`"<link rel="preload" href="init.js" as="script" fetchPriority="low"/><link rel="modulepreload" href="init.mjs" fetchPriority="low"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
8888
);
8989
});
9090

packages/react-dom/src/__tests__/ReactDOMFizzStaticNode-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ describe('ReactDOMFizzStaticNode', () => {
8686
);
8787
const prelude = await readContent(result.prelude);
8888
expect(prelude).toMatchInlineSnapshot(
89-
`"<link rel="preload" href="init.js" as="script"/><link rel="modulepreload" href="init.mjs"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
89+
`"<link rel="preload" href="init.js" as="script" fetchPriority="low"/><link rel="modulepreload" href="init.mjs" fetchPriority="low"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
9090
);
9191
});
9292

packages/react-server-dom-fb/src/__tests__/ReactDOMServerFB-test.internal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ describe('ReactDOMServerFB', () => {
5959
});
6060
const result = readResult(stream);
6161
expect(result).toMatchInlineSnapshot(
62-
`"<link rel="preload" href="init.js" as="script"/><link rel="modulepreload" href="init.mjs"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
62+
`"<link rel="preload" href="init.js" as="script" fetchPriority="low"/><link rel="modulepreload" href="init.mjs" fetchPriority="low"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
6363
);
6464
});
6565

0 commit comments

Comments
 (0)