Skip to content

Commit

Permalink
Preload css (#48840)
Browse files Browse the repository at this point in the history
This PR implements preloading of CSS from RSC.

1. The underlying Flight protocol was extended in
facebook/react#26502 to allow sending hints from
RSC to SSR and Client runtimes. React was updated to include these
changes.
2. We now call `ReactDOM.preload()` for each stylesheet used in a
layout/page layer

There are a few implementation details to take note of
1. we were previously using the `.browser` variant of a few React
packages. This was a holdover from when there was just browser and node
and we wanted to use the browser variant b/c we wanted the same code to
work in edge/node runtimes. React now publishes a `.edge` variant which
is like `.browser` but expects to be server only. This is necessary to
get the opt-in for `AsyncLocalStorage`.
2. Even with the above change, AsyncLocalStorage was not patched on the
global scope until after React was loaded. I moved this into a module
which is loaded first
3. The component passed to RSC's `renderToReadableStream` is not
actually part of the RSC module graph. If I tried to call
`ReactDOM.preload()` inside that function or any other function defined
inside `app-render.tsx` file it would actually see the wrong instance of
`react-dom`. I added a new export on the RSC top level export which
exposes a `preloadStyle(...)` function which just delegates to
`ReactDOM.preload(...)`. This makes the preload run in the right module
graph


~There are a couple of bugs in React that this work uncovered that I
will upstream. We may want to delay merging until they are addressed.
I'll update this comment when that is complete.~
1. ~React, during SSR, can emit a preload for a style twice in some
circumstances because late discovered stylesheets don't consider whether
a preload has already been flushed when preparing to reveal a boundary
they are within~
2. ~React, during RSC updates on the client, can preload a style that is
already in the document because it currently only looks for existing
preload links and does not consider if there is a stylesheet link with
the same href.~

~both of these issues will not break functionality, they just make the
network tab look at bit more noisy. We would expect network deduping to
prevent multiple actual loads~

The above React bugs were fixed and included now in the React update in
this PR

---------

Co-authored-by: Shu Ding <[email protected]>
  • Loading branch information
gnoff and shuding authored Apr 27, 2023
1 parent ffefb31 commit 7a5ef27
Show file tree
Hide file tree
Showing 88 changed files with 13,028 additions and 9,690 deletions.
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,13 @@
"random-seed": "0.3.0",
"react": "18.2.0",
"react-17": "npm:[email protected]",
"react-builtin": "npm:[email protected]c8369527e-20230420",
"react-experimental-builtin": "npm:[email protected]c8369527e-20230420",
"react-builtin": "npm:[email protected]6eadbe0c4-20230425",
"react-experimental-builtin": "npm:[email protected]6eadbe0c4-20230425",
"react-dom": "18.2.0",
"react-dom-17": "npm:[email protected]",
"react-dom-builtin": "npm:[email protected]c8369527e-20230420",
"react-dom-experimental-builtin": "npm:[email protected]c8369527e-20230420",
"react-server-dom-webpack": "18.3.0-next-1f248bdd7-20230419",
"react-dom-builtin": "npm:[email protected]6eadbe0c4-20230425",
"react-dom-experimental-builtin": "npm:[email protected]6eadbe0c4-20230425",
"react-server-dom-webpack": "18.3.0-next-6eadbe0c4-20230425",
"react-ssr-prepass": "1.0.8",
"react-virtualized": "9.22.3",
"relay-compiler": "13.0.2",
Expand All @@ -213,8 +213,8 @@
"rimraf": "3.0.2",
"sass": "1.54.0",
"satori": "0.4.4",
"scheduler-builtin": "npm:[email protected]c8369527e-20230420",
"scheduler-experimental-builtin": "npm:[email protected]c8369527e-20230420",
"scheduler-builtin": "npm:[email protected]6eadbe0c4-20230425",
"scheduler-experimental-builtin": "npm:[email protected]6eadbe0c4-20230425",
"seedrandom": "3.0.5",
"selenium-webdriver": "4.0.0-beta.4",
"semver": "7.3.7",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export { requestAsyncStorage } from 'next/dist/client/components/request-async-s
import * as serverHooks from 'next/dist/client/components/hooks-server-context.js'
export { serverHooks }
export { renderToReadableStream } from 'next/dist/compiled/react-server-dom-webpack/server.edge'
export { preloadStyle } from 'next/dist/server/app-render/rsc/preloads'
1 change: 1 addition & 0 deletions packages/next/src/build/webpack/loaders/next-app-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {
export { renderToReadableStream, decodeReply } from 'next/dist/compiled/react-server-dom-webpack/server.edge'
export const __next_app_webpack_require__ = __webpack_require__
export { preloadStyle } from 'next/dist/server/app-render/rsc/preloads'
`

return result
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ if (process.env.NODE_ENV !== "production") {
(function() {
'use strict';

var ReactVersion = '18.3.0-experimental-c8369527e-20230420';
var ReactVersion = '18.3.0-experimental-6eadbe0c4-20230425';

var Internals = {
usingClientEntryPoint: false,
Expand All @@ -24,41 +24,60 @@ var Internals = {
}
};

function prefetchDNS() {
var dispatcher = Internals.Dispatcher.current;
var Dispatcher = Internals.Dispatcher;
function prefetchDNS(href) {
var passedOptionArg;

{
if (arguments[1] !== undefined) {
passedOptionArg = arguments[1];
}
}

var dispatcher = Dispatcher.current;

if (dispatcher) {
dispatcher.prefetchDNS.apply(this, arguments);
{
if (passedOptionArg !== undefined) {
// prefetchDNS will warn if you pass reserved options arg. We pass it along in Dev only to
// elicit the warning. In prod we do not forward since it is not a part of the interface.
// @TODO move all arg validation into this file. It needs to be universal anyway so may as well lock down the interace here and
// let the rest of the codebase trust the types
dispatcher.prefetchDNS(href, passedOptionArg);
} else {
dispatcher.prefetchDNS(href);
}
}
} // We don't error because preconnect needs to be resilient to being called in a variety of scopes
// and the runtime may not be capable of responding. The function is optimistic and not critical
// so we favor silent bailout over warning or erroring.

}
function preconnect() {
var dispatcher = Internals.Dispatcher.current;
function preconnect(href, options) {
var dispatcher = Dispatcher.current;

if (dispatcher) {
dispatcher.preconnect.apply(this, arguments);
dispatcher.preconnect(href, options);
} // We don't error because preconnect needs to be resilient to being called in a variety of scopes
// and the runtime may not be capable of responding. The function is optimistic and not critical
// so we favor silent bailout over warning or erroring.

}
function preload() {
var dispatcher = Internals.Dispatcher.current;
function preload(href, options) {
var dispatcher = Dispatcher.current;

if (dispatcher) {
dispatcher.preload.apply(this, arguments);
dispatcher.preload(href, options);
} // We don't error because preload needs to be resilient to being called in a variety of scopes
// and the runtime may not be capable of responding. The function is optimistic and not critical
// so we favor silent bailout over warning or erroring.

}
function preinit() {
var dispatcher = Internals.Dispatcher.current;
function preinit(href, options) {
var dispatcher = Dispatcher.current;

if (dispatcher) {
dispatcher.preinit.apply(this, arguments);
dispatcher.preinit(href, options);
} // We don't error because preinit needs to be resilient to being called in a variety of scopes
// and the runtime may not be capable of responding. The function is optimistic and not critical
// so we favor silent bailout over warning or erroring.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';var b={usingClientEntryPoint:!1,Events:null,Dispatcher:{current:null}};function d(a){for(var e="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;c<arguments.length;c++)e+="&args[]="+encodeURIComponent(arguments[c]);return"Minified React error #"+a+"; visit "+e+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=b;
exports.createPortal=function(){throw Error(d(448));};exports.flushSync=function(){throw Error(d(449));};exports.preconnect=function(){var a=b.Dispatcher.current;a&&a.preconnect.apply(this,arguments)};exports.prefetchDNS=function(){var a=b.Dispatcher.current;a&&a.prefetchDNS.apply(this,arguments)};exports.preinit=function(){var a=b.Dispatcher.current;a&&a.preinit.apply(this,arguments)};exports.preload=function(){var a=b.Dispatcher.current;a&&a.preload.apply(this,arguments)};exports.version="18.3.0-experimental-c8369527e-20230420";
'use strict';var d={usingClientEntryPoint:!1,Events:null,Dispatcher:{current:null}};function e(c){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+c,a=1;a<arguments.length;a++)b+="&args[]="+encodeURIComponent(arguments[a]);return"Minified React error #"+c+"; visit "+b+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var f=d.Dispatcher;exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=d;
exports.createPortal=function(){throw Error(e(448));};exports.flushSync=function(){throw Error(e(449));};exports.preconnect=function(c,b){var a=f.current;a&&a.preconnect(c,b)};exports.prefetchDNS=function(c){var b=f.current;b&&b.prefetchDNS(c)};exports.preinit=function(c,b){var a=f.current;a&&a.preinit(c,b)};exports.preload=function(c,b){var a=f.current;a&&a.preload(c,b)};exports.version="18.3.0-experimental-6eadbe0c4-20230425";
Loading

0 comments on commit 7a5ef27

Please sign in to comment.