-
Notifications
You must be signed in to change notification settings - Fork 27.6k
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
feat: added partial shell generation using root params #73816
Conversation
Tests Passed |
dc04d6b
to
af60007
Compare
af60007
to
b0881dd
Compare
Stats from current PRDefault Build (Increase detected
|
vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
---|---|---|---|
buildDuration | 17.8s | 15.5s | N/A |
buildDurationCached | 14.7s | 12.4s | N/A |
nodeModulesSize | 410 MB | 410 MB | |
nextStartRea..uration (ms) | 476ms | 471ms | N/A |
Client Bundles (main, webpack)
vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
---|---|---|---|
1187-HASH.js gzip | 51.1 kB | 51.1 kB | N/A |
8276.HASH.js gzip | 169 B | 168 B | N/A |
8377-HASH.js gzip | 5.36 kB | 5.36 kB | N/A |
bccd1874-HASH.js gzip | 53 kB | 53 kB | N/A |
framework-HASH.js gzip | 57.5 kB | 57.5 kB | N/A |
main-app-HASH.js gzip | 232 B | 235 B | N/A |
main-HASH.js gzip | 34.1 kB | 34 kB | N/A |
webpack-HASH.js gzip | 1.71 kB | 1.71 kB | N/A |
Overall change | 0 B | 0 B | ✓ |
Legacy Client Bundles (polyfills)
vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
---|---|---|---|
polyfills-HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 39.4 kB | 39.4 kB | ✓ |
Client Pages
vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
---|---|---|---|
_app-HASH.js gzip | 193 B | 193 B | ✓ |
_error-HASH.js gzip | 193 B | 193 B | ✓ |
amp-HASH.js gzip | 512 B | 510 B | N/A |
css-HASH.js gzip | 343 B | 342 B | N/A |
dynamic-HASH.js gzip | 1.84 kB | 1.84 kB | ✓ |
edge-ssr-HASH.js gzip | 265 B | 265 B | ✓ |
head-HASH.js gzip | 363 B | 362 B | N/A |
hooks-HASH.js gzip | 393 B | 392 B | N/A |
image-HASH.js gzip | 4.49 kB | 4.49 kB | N/A |
index-HASH.js gzip | 268 B | 268 B | ✓ |
link-HASH.js gzip | 2.35 kB | 2.34 kB | N/A |
routerDirect..HASH.js gzip | 328 B | 328 B | ✓ |
script-HASH.js gzip | 397 B | 397 B | ✓ |
withRouter-HASH.js gzip | 323 B | 326 B | N/A |
1afbb74e6ecf..834.css gzip | 106 B | 106 B | ✓ |
Overall change | 3.59 kB | 3.59 kB | ✓ |
Client Build Manifests
vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
---|---|---|---|
_buildManifest.js gzip | 749 B | 746 B | N/A |
Overall change | 0 B | 0 B | ✓ |
Rendered Page Sizes
vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
---|---|---|---|
index.html gzip | 524 B | 523 B | N/A |
link.html gzip | 539 B | 537 B | N/A |
withRouter.html gzip | 520 B | 519 B | N/A |
Overall change | 0 B | 0 B | ✓ |
Edge SSR bundle Size
vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
---|---|---|---|
edge-ssr.js gzip | 128 kB | 128 kB | N/A |
page.js gzip | 204 kB | 204 kB | N/A |
Overall change | 0 B | 0 B | ✓ |
Middleware size
vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
---|---|---|---|
middleware-b..fest.js gzip | 672 B | 668 B | N/A |
middleware-r..fest.js gzip | 155 B | 156 B | N/A |
middleware.js gzip | 31.2 kB | 31.2 kB | N/A |
edge-runtime..pack.js gzip | 844 B | 844 B | ✓ |
Overall change | 844 B | 844 B | ✓ |
Next Runtimes
vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
---|---|---|---|
523-experime...dev.js gzip | 322 B | 322 B | ✓ |
523.runtime.dev.js gzip | 314 B | 314 B | ✓ |
app-page-exp...dev.js gzip | 323 kB | 323 kB | N/A |
app-page-exp..prod.js gzip | 127 kB | 127 kB | ✓ |
app-page-tur..prod.js gzip | 140 kB | 140 kB | ✓ |
app-page-tur..prod.js gzip | 135 kB | 135 kB | ✓ |
app-page.run...dev.js gzip | 314 kB | 314 kB | N/A |
app-page.run..prod.js gzip | 123 kB | 123 kB | ✓ |
app-route-ex...dev.js gzip | 37.4 kB | 37.4 kB | ✓ |
app-route-ex..prod.js gzip | 25.5 kB | 25.5 kB | ✓ |
app-route-tu..prod.js gzip | 25.5 kB | 25.5 kB | ✓ |
app-route-tu..prod.js gzip | 25.3 kB | 25.3 kB | ✓ |
app-route.ru...dev.js gzip | 39 kB | 39 kB | ✓ |
app-route.ru..prod.js gzip | 25.3 kB | 25.3 kB | ✓ |
pages-api-tu..prod.js gzip | 9.69 kB | 9.69 kB | ✓ |
pages-api.ru...dev.js gzip | 11.6 kB | 11.6 kB | ✓ |
pages-api.ru..prod.js gzip | 9.68 kB | 9.68 kB | ✓ |
pages-turbo...prod.js gzip | 21.7 kB | 21.7 kB | ✓ |
pages.runtim...dev.js gzip | 27.4 kB | 27.4 kB | ✓ |
pages.runtim..prod.js gzip | 21.7 kB | 21.7 kB | ✓ |
server.runti..prod.js gzip | 916 kB | 916 kB | ✓ |
Overall change | 1.72 MB | 1.72 MB | ✓ |
build cache Overall increase ⚠️
vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
---|---|---|---|
0.pack gzip | 2.06 MB | 2.06 MB | |
index.pack gzip | 72.7 kB | 73 kB | |
Overall change | 2.13 MB | 2.13 MB |
Diff details
Diff for middleware.js
Diff too large to display
Diff for edge-ssr.js
Diff too large to display
Diff for main-HASH.js
Diff too large to display
d04aae1
to
c4663e6
Compare
df2bca0
to
32957e5
Compare
efa6905
to
2c87c16
Compare
packages/next/src/build/segment-config/app/collect-root-param-keys.ts
Outdated
Show resolved
Hide resolved
acb7423
to
c957272
Compare
2c87c16
to
0520878
Compare
0520878
to
0f4adf8
Compare
c957272
to
d410536
Compare
0f4adf8
to
9cb16ca
Compare
d410536
to
f72a407
Compare
60a36f3
to
b51ca16
Compare
f72a407
to
affef26
Compare
b51ca16
to
386d2ec
Compare
affef26
to
ad7b0a5
Compare
386d2ec
to
18cb80b
Compare
} | ||
|
||
/** | ||
* Collects the segments for a given route module. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like the comment here needs to be updated to reflect that this function is collecting root params
for (const params of routeParams) { | ||
let i = 0 | ||
for (; i < unique.length; i++) { | ||
const item = unique[i] | ||
let j = 0 | ||
for (; j < paramKeys.length; j++) { | ||
const key = paramKeys[j] | ||
|
||
// If the param is not the same, then we need to break out of the loop. | ||
if (!areParamValuesEqual(item[key], params[key])) { | ||
break | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(non-blocking nit)
this function is a pretty clever usage of loops but I worry that what it makes up for in performance it sacrifices in readability. I'm also not 100% sure that this is more performant than the more expressive version, eg:
for (const params of routeParams) {
const isDuplicate = unique.some((item) =>
paramKeys.every((key) => areParamValuesEqual(item[key], params[key]))
);
if (!isDuplicate) {
unique.push(params);
}
}
In the worst case, this is O(N * M * K), which I think matches the implementation here as well:
for each params in routeParams (N times):
for each item in unique (M times):
for each key in paramKeys (K times):
// perform comparison
(I actually am curious if the built-in loops that power some
/every
might be slightly more efficient?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only downside to the .some.every
version is that each loop of N
, we create 2 functions, which is avoided with the looping. I'll defer to @gnoff if this seems alright 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm partial to the loop code b/c it should be faster both in internal implementation and b/c it creates fewer functions. Typlically for loops can be heavily optimized by JIT and the builtins are only optimized maybe in the newest runtimes and I suspect not likely nearly as much as raw loops. That said I think you can achieve better complexity by tracking the param 'key' becuase the routeParams are in order and spaces aren't allowed.
for /app/[foo]/[bar]/page.tsx
we'd encode
{}
-> ''
{ foo: 'f0' }
-> 'f0'
{ bar: 'b1', foo: 'f1' }
-> 'f1 b1'
{ foo: 'f2', bar: 'b2' }
-> 'f2 b2'
And so if you later encountered { bar: 'b2', foo: 'f2' }
(same values, different object key order) you'd just recompute 'f2 b2'
and see you already produced this result
It doesn't even have to be a string you could also have Maps of Maps but this has fewer objects created
The interesting question is what happens if you had just { bar: 'b3' }
. This would really be treated like unknown params from the foo downwards so it should encode as ''
since the bar can't be "reached" except if there was a known foo value.
f5d10b6
to
20b4910
Compare
18cb80b
to
e689a21
Compare
22d9c2c
to
21f813f
Compare
e689a21
to
acb0ab9
Compare
acb0ab9
to
28c6ea2
Compare
21f813f
to
ff0ef9c
Compare
28c6ea2
to
d54fc94
Compare
Merge activity
|
This enables the generation of partial shells when using with partial prerendering, aided by the new
rootParams()
API. Essentially, when your application only returns partial routes (where not all the route parameters are known), Next.js will now build route shells for these pages. We call these shells, fallback shells. They represent the partial state of the page that once served to the user, will provide a more complete loading experience as fast as possible.We'll also take every permutation of the provided root params and generate a shell just for them to ensure that any future calls to those routes will be able to use the more specific fallback shell that's generated rather than having to rely on a blank shell by default.