forked from vercel/next.js
-
Notifications
You must be signed in to change notification settings - Fork 0
[pull] canary from vercel:canary #241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Initial implementation of runtime prefetching. A "runtime prefetch" is a more complete version of a static prefetch (i.e. the one that a link does by default), rendered on demand, and not cached server-side. It will render the server part of the page dynamically, allowing the usage of - `params` and `searchParams` - `cookies()` - `"use cache: private"` (these are omitted from static prerenders) - `"use cache"` with a short expire time (these are omitted from static prerenders) The result may be partial (in the PPR sense). It will exclude any parts of the page that depend on - uncached IO - `connection()`, `headers()` This allows the client router to cache the result, because it has a well-defined stale time. Note that public caches with a stale time below a certain fixed treshold will also be excluded, because it wouldn't make sense to keep them around in the router cache if we need to throw them away soon after getting them. With this PR, `<Link prefetch={true}>` changes meaning if `clientSegmentCache` + `cacheComponents` are enabled. It will now initiate a runtime prefetch instead of a "full" prefetch, which included everything that a navigation request would. Full prefetches can be done via `<Link prefetch="unstable_forceStale">`. If only one of the two flags is on, the behavior of `<Link prefetch={true}>` is unchanged from how it currently works. I've split the changes up into separate commits for ease of review: 1. Introducing the new workUnitStore type 2. Server - handling the prefetch header and rendering 3. Client - Link and segment cache changes ### Implementation notes The client router sends `next-router-prefetch: 2` to signal that it wants a runtime prefetch (as opposed to the old `next-router-prefetch: 1`, which is used for static prefetches). > NOTE: this builder change is required for this to work on vercel vercel/vercel#13547. It was released in `[email protected]` Somewhat confusingly, in order to to avoid existing static prefetch codepaths, we need the server to _not_ treat this as "a prefetch request". Instead, we want to mostly treat this like we would a navigation request, and render dynamically. This means that: - `isPrefetchRequest` (from `parseRequestHeaders` in `app-render`) will be `false` - `getRequestMeta(req, 'isPrefetchRSCRequest')` won't be set This is a bit ugly but it works for now. I'll try to clean it up in the future. We render a payload of the same shape as a navigation request (including omitting shared layouts, as instructed by the `Next-Router-State-Tree` header). But unlike a navigation request, we do a cache-components-style prerender at runtime in order to exclude uncached/sync IO. This prerender uses a new workUnitStore type, `'prerender-runtime'`. This store type changes the behavior of `cookies()`, `params`, `searchParams`, `"use cache: private"`, `next/root-params`, and others. Unlike a static prerender, if we detect a bad uncached/sync IO usage, we just log an error (instead of throwing it and erroring) and respond with whatever we managed to render up until the render was aborted, in hopes that we can still return something useful to the client. This request is happening at runtime, so we should try to handle errors gracefully. We track whether or not the prerender has any dynamic holes, and if it does, set `x-nextjs-postponed: 1` on the response. This tells the client router if we still need to fetch more data when navigating, or if we can skip it because we already have a complete page. Ideally, we'd track this information per-segment for better reuse on the client side, but that's not in scope for this PR. We also set the `x-nextjs-staletime` header on the response to tell the client router how long it should keep this prefetch in the cache. Note that this does not affect `Cache-Control`, which should still be the same as a dynamic navigation request to prevent it from being cached by anything other than the client router. This may be improved in the future if it turns out we can safely set an appropriate `Cache-Control: private, ...` that also accounts for e.g. changing cookie values, but i'm erring on the side of caution for now.
This is part of a larger effort to remove dynamic params from server responses except in cases where they are needed to render a Server Component. If a param is not rendered by a Server Component, then it can be omitted from the cache key, and cached responses can be reused across pages with different param values. In this step, I've implemented client parsing of the params from the response headers. The basic approach is to split the URL into parts, then traverse the route tree to pick off the param values, taking care to skip over things like interception routes. Notably, this is not how the server parses param values. The server gets the params from the regex that's also used for routing. Originally, I thought I'd send the regex to the client and use it there, too. However, this ended up being needlessly complicated, because the server regexes are also used for things like interception route matching. But this is already encapsulated by the structure of the route tree. So it's easier to just walk the tree and pick off the params. My main hesitation is that this introduces some risk of drift between the server and client params parsing; we'll need to keep them in sync. However, I think the solution is actually to update the server to also pick the params off the URL, rather than use the ones passed from the base router. It's conceptually cleaner, since it's less likely that extra concerns from the base server leaking into the application layer. As an example, compare the code needed to get a catchall param value in the [client](https://github.com/acdlite/next.js/blob/09b16a816ff4d595b5111cb1a6de540838caf97e/packages/next/src/client/client-params.ts#L57-L61) versus the [server](https://github.com/acdlite/next.js/blob/09b16a816ff4d595b5111cb1a6de540838caf97e/packages/next/src/shared/lib/router/utils/get-dynamic-param.ts#L37-L84) implementation. Note: Although the ultimate goal is to remove the dynamic params from the response body (e.g. FlightRouterState), I have not done so yet this PR. The rest of the work will be split up into multiple subsequent PRs.
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.3)
Can you help keep this open source service alive? 💖 Please sponsor : )