Skip to content

Commit

Permalink
[Fizz] declare bootstrap script preloads to be fetchPriority: 'low' (
Browse files Browse the repository at this point in the history
…#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
  • Loading branch information
gnoff committed Aug 7, 2023
1 parent ea17cc1 commit 9edf470
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 24 deletions.
13 changes: 11 additions & 2 deletions packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -4245,6 +4245,8 @@ export function writePreamble(
// Flush unblocked stylesheets by precedence
resources.precedences.forEach(flushAllStylesInPreamble, destination);

resources.bootstrapScripts.forEach(flushResourceInPreamble, destination);

resources.scripts.forEach(flushResourceInPreamble, destination);
resources.scripts.clear();

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

// bootstrap scripts should flush above script priority but these can only flush in the preamble
// so we elide the code here for performance

resources.scripts.forEach(flushResourceLate, destination);
resources.scripts.clear();

Expand Down Expand Up @@ -4875,6 +4880,7 @@ export type Resources = {
// usedImagePreloads: Set<PreloadResource>,
precedences: Map<string, Set<StyleResource>>,
stylePrecedences: Map<string, StyleTagResource>,
bootstrapScripts: Set<PreloadResource>,
scripts: Set<ScriptResource>,
explicitStylesheetPreloads: Set<PreloadResource>,
// explicitImagePreloads: Set<PreloadResource>,
Expand All @@ -4901,6 +4907,7 @@ export function createResources(): Resources {
// usedImagePreloads: new Set(),
precedences: new Map(),
stylePrecedences: new Map(),
bootstrapScripts: new Set(),
scripts: new Set(),
explicitStylesheetPreloads: new Set(),
// explicitImagePreloads: new Set(),
Expand Down Expand Up @@ -5470,6 +5477,7 @@ function preloadBootstrapScript(
rel: 'preload',
href: src,
as: 'script',
fetchPriority: 'low',
nonce,
integrity,
crossOrigin,
Expand All @@ -5481,7 +5489,7 @@ function preloadBootstrapScript(
props,
};
resources.preloadsMap.set(key, resource);
resources.explicitScriptPreloads.add(resource);
resources.bootstrapScripts.add(resource);
pushLinkImpl(resource.chunks, props);
}

Expand Down Expand Up @@ -5511,6 +5519,7 @@ function preloadBootstrapModule(
const props: PreloadModuleProps = {
rel: 'modulepreload',
href: src,
fetchPriority: 'low',
nonce,
integrity,
crossOrigin,
Expand All @@ -5522,7 +5531,7 @@ function preloadBootstrapModule(
props,
};
resources.preloadsMap.set(key, resource);
resources.explicitScriptPreloads.add(resource);
resources.bootstrapScripts.add(resource);
pushLinkImpl(resource.chunks, props);
return;
}
Expand Down
82 changes: 66 additions & 16 deletions packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -610,17 +610,30 @@ describe('ReactDOMFizzServer', () => {
});

expect(getVisibleChildren(container)).toEqual([
<link rel="preload" href="init.js" as="script" nonce={CSPnonce} />,
<link
rel="preload"
fetchpriority="low"
href="init.js"
as="script"
nonce={CSPnonce}
/>,
<link
rel="preload"
fetchpriority="low"
href="init2.js"
as="script"
nonce={CSPnonce}
integrity="init2hash"
/>,
<link rel="modulepreload" href="init.mjs" nonce={CSPnonce} />,
<link
rel="modulepreload"
fetchpriority="low"
href="init.mjs"
nonce={CSPnonce}
/>,
<link
rel="modulepreload"
fetchpriority="low"
href="init2.mjs"
nonce={CSPnonce}
integrity="init2hash"
Expand All @@ -640,17 +653,30 @@ describe('ReactDOMFizzServer', () => {
resolve({default: Text});
});
expect(getVisibleChildren(container)).toEqual([
<link rel="preload" href="init.js" as="script" nonce={CSPnonce} />,
<link
rel="preload"
fetchpriority="low"
href="init.js"
as="script"
nonce={CSPnonce}
/>,
<link
rel="preload"
fetchpriority="low"
href="init2.js"
as="script"
nonce={CSPnonce}
integrity="init2hash"
/>,
<link rel="modulepreload" href="init.mjs" nonce={CSPnonce} />,
<link
rel="modulepreload"
fetchpriority="low"
href="init.mjs"
nonce={CSPnonce}
/>,
<link
rel="modulepreload"
fetchpriority="low"
href="init2.mjs"
nonce={CSPnonce}
integrity="init2hash"
Expand Down Expand Up @@ -3797,12 +3823,23 @@ describe('ReactDOMFizzServer', () => {
expect(getVisibleChildren(document)).toEqual(
<html>
<head>
<link rel="preload" href="foo" as="script" />
<link rel="preload" href="bar" as="script" />
<link rel="preload" href="baz" as="script" integrity="qux" />
<link rel="modulepreload" href="quux" />
<link rel="modulepreload" href="corge" />
<link rel="modulepreload" href="grault" integrity="garply" />
<link rel="preload" fetchpriority="low" href="foo" as="script" />
<link rel="preload" fetchpriority="low" href="bar" as="script" />
<link
rel="preload"
fetchpriority="low"
href="baz"
as="script"
integrity="qux"
/>
<link rel="modulepreload" fetchpriority="low" href="quux" />
<link rel="modulepreload" fetchpriority="low" href="corge" />
<link
rel="modulepreload"
fetchpriority="low"
href="grault"
integrity="garply"
/>
</head>
<body>
<div>hello world</div>
Expand Down Expand Up @@ -3866,14 +3903,27 @@ describe('ReactDOMFizzServer', () => {
expect(getVisibleChildren(document)).toEqual(
<html>
<head>
<link rel="preload" href="foo" as="script" />
<link rel="preload" href="bar" as="script" />
<link rel="preload" href="baz" as="script" crossorigin="" />
<link rel="preload" href="qux" as="script" crossorigin="" />
<link rel="modulepreload" href="quux" />
<link rel="modulepreload" href="corge" />
<link rel="preload" fetchpriority="low" href="foo" as="script" />
<link rel="preload" fetchpriority="low" href="bar" as="script" />
<link
rel="preload"
fetchpriority="low"
href="baz"
as="script"
crossorigin=""
/>
<link
rel="preload"
fetchpriority="low"
href="qux"
as="script"
crossorigin=""
/>
<link rel="modulepreload" fetchpriority="low" href="quux" />
<link rel="modulepreload" fetchpriority="low" href="corge" />
<link
rel="modulepreload"
fetchpriority="low"
href="grault"
crossorigin="use-credentials"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ describe('ReactDOMFizzServerBrowser', () => {
);
const result = await readResult(stream);
expect(result).toMatchInlineSnapshot(
`"<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>"`,
`"<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>"`,
);
});

Expand Down Expand Up @@ -500,7 +500,7 @@ describe('ReactDOMFizzServerBrowser', () => {
);
const result = await readResult(stream);
expect(result).toMatchInlineSnapshot(
`"<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>"`,
`"<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>"`,
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ describe('ReactDOMFizzServerNode', () => {
pipe(writable);
jest.runAllTimers();
expect(output.result).toMatchInlineSnapshot(
`"<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>"`,
`"<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>"`,
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ describe('ReactDOMFizzStaticBrowser', () => {
});
const prelude = await readContent(result.prelude);
expect(prelude).toMatchInlineSnapshot(
`"<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>"`,
`"<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>"`,
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe('ReactDOMFizzStaticNode', () => {
);
const prelude = await readContent(result.prelude);
expect(prelude).toMatchInlineSnapshot(
`"<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>"`,
`"<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>"`,
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('ReactDOMServerFB', () => {
});
const result = readResult(stream);
expect(result).toMatchInlineSnapshot(
`"<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>"`,
`"<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>"`,
);
});

Expand Down

0 comments on commit 9edf470

Please sign in to comment.