From eddb096d4b9badd4f35b200a2f28049515d5e76f Mon Sep 17 00:00:00 2001 From: gnoff Date: Mon, 7 Aug 2023 22:50:28 +0000 Subject: [PATCH] [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 DiffTrain build for [9edf470d6ed1f3ac12c23a248e64293f367c1797](https://github.com/facebook/react/commit/9edf470d6ed1f3ac12c23a248e64293f367c1797) --- compiled/facebook-www/REVISION | 2 +- .../facebook-www/ReactDOMServer-dev.classic.js | 14 ++++++++++---- compiled/facebook-www/ReactDOMServer-dev.modern.js | 14 ++++++++++---- .../facebook-www/ReactDOMServer-prod.classic.js | 7 ++++++- .../facebook-www/ReactDOMServer-prod.modern.js | 7 ++++++- .../ReactDOMServerStreaming-dev.modern.js | 12 +++++++++--- .../ReactDOMServerStreaming-prod.modern.js | 11 +++++++++-- 7 files changed, 51 insertions(+), 16 deletions(-) diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index 9ada185e55..f3734da4e1 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -ea17cc18f458010c89c1bf494be08bb782d034af +9edf470d6ed1f3ac12c23a248e64293f367c1797 diff --git a/compiled/facebook-www/ReactDOMServer-dev.classic.js b/compiled/facebook-www/ReactDOMServer-dev.classic.js index f03937a2a0..90d54313ed 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.classic.js +++ b/compiled/facebook-www/ReactDOMServer-dev.classic.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); -var ReactVersion = "18.3.0-www-classic-8651ccc3"; +var ReactVersion = "18.3.0-www-classic-728bbc0a"; // This refers to a WWW module. var warningWWW = require("warning"); @@ -6282,6 +6282,7 @@ function writePreamble( resources.fontPreloads.clear(); // Flush unblocked stylesheets by precedence resources.precedences.forEach(flushAllStylesInPreamble, destination); + resources.bootstrapScripts.forEach(flushResourceInPreamble, destination); resources.scripts.forEach(flushResourceInPreamble, destination); resources.scripts.clear(); resources.explicitStylesheetPreloads.forEach( @@ -6346,7 +6347,9 @@ function writeHoistables(destination, resources, responseState) { resources.fontPreloads.clear(); // Preload any stylesheets. these will emit in a render instruction that follows this // but we want to kick off preloading as soon as possible - resources.precedences.forEach(preloadLateStyles, destination); + 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(); resources.explicitStylesheetPreloads.forEach(flushResourceLate, destination); @@ -6836,6 +6839,7 @@ function createResources() { // usedImagePreloads: new Set(), precedences: new Map(), stylePrecedences: new Map(), + bootstrapScripts: new Set(), scripts: new Set(), explicitStylesheetPreloads: new Set(), // explicitImagePreloads: new Set(), @@ -7482,6 +7486,7 @@ function preloadBootstrapScript(resources, src, nonce, integrity, crossOrigin) { rel: "preload", href: src, as: "script", + fetchPriority: "low", nonce: nonce, integrity: integrity, crossOrigin: crossOrigin @@ -7493,7 +7498,7 @@ function preloadBootstrapScript(resources, src, nonce, integrity, crossOrigin) { props: props }; resources.preloadsMap.set(key, resource); - resources.explicitScriptPreloads.add(resource); + resources.bootstrapScripts.add(resource); pushLinkImpl(resource.chunks, props); } // This function is only safe to call at Request start time since it assumes // that each module has not already been preloaded. If we find a need to preload @@ -7518,6 +7523,7 @@ function preloadBootstrapModule(resources, src, nonce, integrity, crossOrigin) { var props = { rel: "modulepreload", href: src, + fetchPriority: "low", nonce: nonce, integrity: integrity, crossOrigin: crossOrigin @@ -7529,7 +7535,7 @@ function preloadBootstrapModule(resources, src, nonce, integrity, crossOrigin) { props: props }; resources.preloadsMap.set(key, resource); - resources.explicitScriptPreloads.add(resource); + resources.bootstrapScripts.add(resource); pushLinkImpl(resource.chunks, props); return; } diff --git a/compiled/facebook-www/ReactDOMServer-dev.modern.js b/compiled/facebook-www/ReactDOMServer-dev.modern.js index fa3af810e7..b899d328b6 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.modern.js +++ b/compiled/facebook-www/ReactDOMServer-dev.modern.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); -var ReactVersion = "18.3.0-www-modern-f39e0868"; +var ReactVersion = "18.3.0-www-modern-9727d465"; // This refers to a WWW module. var warningWWW = require("warning"); @@ -6282,6 +6282,7 @@ function writePreamble( resources.fontPreloads.clear(); // Flush unblocked stylesheets by precedence resources.precedences.forEach(flushAllStylesInPreamble, destination); + resources.bootstrapScripts.forEach(flushResourceInPreamble, destination); resources.scripts.forEach(flushResourceInPreamble, destination); resources.scripts.clear(); resources.explicitStylesheetPreloads.forEach( @@ -6346,7 +6347,9 @@ function writeHoistables(destination, resources, responseState) { resources.fontPreloads.clear(); // Preload any stylesheets. these will emit in a render instruction that follows this // but we want to kick off preloading as soon as possible - resources.precedences.forEach(preloadLateStyles, destination); + 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(); resources.explicitStylesheetPreloads.forEach(flushResourceLate, destination); @@ -6836,6 +6839,7 @@ function createResources() { // usedImagePreloads: new Set(), precedences: new Map(), stylePrecedences: new Map(), + bootstrapScripts: new Set(), scripts: new Set(), explicitStylesheetPreloads: new Set(), // explicitImagePreloads: new Set(), @@ -7482,6 +7486,7 @@ function preloadBootstrapScript(resources, src, nonce, integrity, crossOrigin) { rel: "preload", href: src, as: "script", + fetchPriority: "low", nonce: nonce, integrity: integrity, crossOrigin: crossOrigin @@ -7493,7 +7498,7 @@ function preloadBootstrapScript(resources, src, nonce, integrity, crossOrigin) { props: props }; resources.preloadsMap.set(key, resource); - resources.explicitScriptPreloads.add(resource); + resources.bootstrapScripts.add(resource); pushLinkImpl(resource.chunks, props); } // This function is only safe to call at Request start time since it assumes // that each module has not already been preloaded. If we find a need to preload @@ -7518,6 +7523,7 @@ function preloadBootstrapModule(resources, src, nonce, integrity, crossOrigin) { var props = { rel: "modulepreload", href: src, + fetchPriority: "low", nonce: nonce, integrity: integrity, crossOrigin: crossOrigin @@ -7529,7 +7535,7 @@ function preloadBootstrapModule(resources, src, nonce, integrity, crossOrigin) { props: props }; resources.preloadsMap.set(key, resource); - resources.explicitScriptPreloads.add(resource); + resources.bootstrapScripts.add(resource); pushLinkImpl(resource.chunks, props); return; } diff --git a/compiled/facebook-www/ReactDOMServer-prod.classic.js b/compiled/facebook-www/ReactDOMServer-prod.classic.js index b5feb54821..ffca8df28d 100644 --- a/compiled/facebook-www/ReactDOMServer-prod.classic.js +++ b/compiled/facebook-www/ReactDOMServer-prod.classic.js @@ -3649,6 +3649,10 @@ function flushCompletedQueues(request, destination) { resources.fontPreloads.forEach(flushResourceInPreamble, destination); resources.fontPreloads.clear(); resources.precedences.forEach(flushAllStylesInPreamble, destination); + resources.bootstrapScripts.forEach( + flushResourceInPreamble, + destination + ); resources.scripts.forEach(flushResourceInPreamble, destination); resources.scripts.clear(); resources.explicitStylesheetPreloads.forEach( @@ -3949,6 +3953,7 @@ function renderToStringImpl( fontPreloads: new Set(), precedences: new Map(), stylePrecedences: new Map(), + bootstrapScripts: new Set(), scripts: new Set(), explicitStylesheetPreloads: new Set(), explicitScriptPreloads: new Set(), @@ -4014,4 +4019,4 @@ exports.renderToString = function (children, options) { 'The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server' ); }; -exports.version = "18.3.0-www-classic-849751e4"; +exports.version = "18.3.0-www-classic-e05f0ecc"; diff --git a/compiled/facebook-www/ReactDOMServer-prod.modern.js b/compiled/facebook-www/ReactDOMServer-prod.modern.js index 9f75197926..8aab06f193 100644 --- a/compiled/facebook-www/ReactDOMServer-prod.modern.js +++ b/compiled/facebook-www/ReactDOMServer-prod.modern.js @@ -3547,6 +3547,10 @@ function flushCompletedQueues(request, destination) { resources.fontPreloads.forEach(flushResourceInPreamble, destination); resources.fontPreloads.clear(); resources.precedences.forEach(flushAllStylesInPreamble, destination); + resources.bootstrapScripts.forEach( + flushResourceInPreamble, + destination + ); resources.scripts.forEach(flushResourceInPreamble, destination); resources.scripts.clear(); resources.explicitStylesheetPreloads.forEach( @@ -3847,6 +3851,7 @@ function renderToStringImpl( fontPreloads: new Set(), precedences: new Map(), stylePrecedences: new Map(), + bootstrapScripts: new Set(), scripts: new Set(), explicitStylesheetPreloads: new Set(), explicitScriptPreloads: new Set(), @@ -3912,4 +3917,4 @@ exports.renderToString = function (children, options) { 'The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server' ); }; -exports.version = "18.3.0-www-modern-a94e9f5e"; +exports.version = "18.3.0-www-modern-432efc32"; diff --git a/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js b/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js index f17d743d40..5fed639f6e 100644 --- a/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js +++ b/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js @@ -6289,6 +6289,7 @@ function writePreamble( resources.fontPreloads.clear(); // Flush unblocked stylesheets by precedence resources.precedences.forEach(flushAllStylesInPreamble, destination); + resources.bootstrapScripts.forEach(flushResourceInPreamble, destination); resources.scripts.forEach(flushResourceInPreamble, destination); resources.scripts.clear(); resources.explicitStylesheetPreloads.forEach( @@ -6353,7 +6354,9 @@ function writeHoistables(destination, resources, responseState) { resources.fontPreloads.clear(); // Preload any stylesheets. these will emit in a render instruction that follows this // but we want to kick off preloading as soon as possible - resources.precedences.forEach(preloadLateStyles, destination); + 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(); resources.explicitStylesheetPreloads.forEach(flushResourceLate, destination); @@ -6843,6 +6846,7 @@ function createResources() { // usedImagePreloads: new Set(), precedences: new Map(), stylePrecedences: new Map(), + bootstrapScripts: new Set(), scripts: new Set(), explicitStylesheetPreloads: new Set(), // explicitImagePreloads: new Set(), @@ -7489,6 +7493,7 @@ function preloadBootstrapScript(resources, src, nonce, integrity, crossOrigin) { rel: "preload", href: src, as: "script", + fetchPriority: "low", nonce: nonce, integrity: integrity, crossOrigin: crossOrigin @@ -7500,7 +7505,7 @@ function preloadBootstrapScript(resources, src, nonce, integrity, crossOrigin) { props: props }; resources.preloadsMap.set(key, resource); - resources.explicitScriptPreloads.add(resource); + resources.bootstrapScripts.add(resource); pushLinkImpl(resource.chunks, props); } // This function is only safe to call at Request start time since it assumes // that each module has not already been preloaded. If we find a need to preload @@ -7525,6 +7530,7 @@ function preloadBootstrapModule(resources, src, nonce, integrity, crossOrigin) { var props = { rel: "modulepreload", href: src, + fetchPriority: "low", nonce: nonce, integrity: integrity, crossOrigin: crossOrigin @@ -7536,7 +7542,7 @@ function preloadBootstrapModule(resources, src, nonce, integrity, crossOrigin) { props: props }; resources.preloadsMap.set(key, resource); - resources.explicitScriptPreloads.add(resource); + resources.bootstrapScripts.add(resource); pushLinkImpl(resource.chunks, props); return; } diff --git a/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js b/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js index 2ec02677dd..132c997098 100644 --- a/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js +++ b/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js @@ -3409,6 +3409,10 @@ function flushCompletedQueues(request, destination) { resources.fontPreloads.forEach(flushResourceInPreamble, destination); resources.fontPreloads.clear(); resources.precedences.forEach(flushAllStylesInPreamble, destination); + resources.bootstrapScripts.forEach( + flushResourceInPreamble, + destination + ); resources.scripts.forEach(flushResourceInPreamble, destination); resources.scripts.clear(); resources.explicitStylesheetPreloads.forEach( @@ -3819,6 +3823,7 @@ exports.renderToStream = function (children, options) { fontPreloads: new Set(), precedences: new Map(), stylePrecedences: new Map(), + bootstrapScripts: new Set(), scripts: new Set(), explicitStylesheetPreloads: new Set(), explicitScriptPreloads: new Set(), @@ -3883,13 +3888,14 @@ exports.renderToStream = function (children, options) { rel: "preload", href: externalRuntimeConfig, as: "script", + fetchPriority: "low", nonce: void 0, integrity: integrity, crossOrigin: scriptConfig }, resource = { type: "preload", chunks: [], state: 0, props: props }; resources.preloadsMap.set("[script]" + externalRuntimeConfig, resource); - resources.explicitScriptPreloads.add(resource); + resources.bootstrapScripts.add(resource); pushLinkImpl(resource.chunks, props); JSCompiler_inline_result.push( '