You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Like every PR in this series, this was profiling the impacted method before and after the change. For example we can see in the example below that the `buildLocation` method went from being one of the major bottlenecks of a navigation to being a very small part of the overall cost:
136
+
Like every PR in this series, this change was validates by profiling the impacted method before and after. For example we can see in the example below that the `buildLocation` method went from being one of the major bottlenecks of a navigation to being a very small part of the overall cost:
| Before ||
184
+
| After ||
185
+
179
186
## Finding 3: server-only fast paths are worth it (when gated correctly)
180
187
181
188
### The mechanism
@@ -188,7 +195,7 @@ If you can guard a branch with a **build-time constant** like `isServer`, you ca
188
195
- keep the general algorithm for correctness and edge cases
189
196
- allow bundlers to delete the server-only branch from client builds
190
197
191
-
In TanStack Start, `isServer` is provided via build-time resolution (client: `false`, server: `true`, dev/test: `undefined` with fallback). Modern bundlers like Vite, Rollup, and esbuild perform dead code elimination (DCE)[^dce], removing unreachable branches when the condition is a compile-time constant.
198
+
In TanStack Start, `isServer` is provided via build-time resolution of export conditions[^export-conditions](client: `false`, server: `true`, dev/test: `undefined` with fallback). Modern bundlers like Vite, Rollup, and esbuild perform dead code elimination (DCE)[^dce], removing unreachable branches when the condition is a compile-time constant.
192
199
193
200
### The transferable pattern
194
201
@@ -197,7 +204,7 @@ Write two implementations:
197
204
-**fast path** for the common case
198
205
-**general path** for correctness
199
206
200
-
And gate them behind a build-time constant so you don't ship server-only logic to clients.
207
+
And gate them behind a build-time constant so you don't inflate the bundle size for clients.
201
208
202
209
### What we changed
203
210
@@ -208,9 +215,11 @@ And gate them behind a build-time constant so you don't ship server-only logic t
208
215
209
216
if (isServer) {
210
217
// server-only fast path (removed from client bundle)
211
-
returnfastServerPath(input)
218
+
if (isCommonCase(input)) {
219
+
returnfastServerPath(input)
220
+
}
212
221
}
213
-
// general algorithm (used on client, fallback on server in dev)
| Before ||
260
+
| After ||
261
+
246
262
## Results
247
263
248
264
Benchmark: placeholder text, should link to Matteo's article.
@@ -267,15 +283,15 @@ The following graphs show event-loop utilization against throughput for each fea
267
283
268
284
#### links-100
269
285
270
-

286
+

271
287
272
288
#### layouts-26-with-params
273
289
274
-

290
+

275
291
276
292
#### empty (baseline)
277
293
278
-

294
+

279
295
280
296
### Flamegraph evidence slots
281
297
@@ -304,10 +320,12 @@ There were many other improvements (client and server) not covered here. SSR per
304
320
305
321
[^webkit-delete-ic]: WebKit, "A Tour of Inline Caching with Delete" `https://webkit.org/blog/10298/inline-caching-delete/`
306
322
307
-
[^structural-sharing]: Structural sharing is a pattern from immutable data libraries (Immer, React Query, TanStack Store) where unchanged portions of data structures are reused by reference to minimize allocation and enable cheap equality checks.
323
+
[^structural-sharing]: Structural sharing is a pattern from immutable data libraries (Immer, React Query, TanStack Store) where unchanged portions of data structures are reused by reference enable cheap equality checks. See [Structural Sharing](https://tanstack.com/query/latest/docs/framework/react/guides/render-optimizations#structural-sharing) in the TanStack Query documentation.
308
324
309
-
[^ssr-streaming]: With streaming SSR and Suspense, the server may render multiple chunks, but each chunk is still a single-pass render with no reactive updates.
325
+
[^ssr-streaming]: With streaming SSR and Suspense, the server may render multiple chunks, but each chunk is still a single-pass render with no reactive updates. See [renderToPipeableStream](https://react.dev/reference/react-dom/server/renderToPipeableStream) in the React documentation.
310
326
311
327
[^url-cost]: The WHATWG URL Standard requires significant parsing work: scheme detection, authority parsing, path normalization, query string handling, and percent-encoding. See the [URL parsing algorithm](https://url.spec.whatwg.org/#url-parsing) for the full state machine.
312
328
313
-
[^dce]: Dead code elimination is a standard compiler optimization. See esbuild's documentation on [tree shaking](https://esbuild.github.io/api/#tree-shaking) and Rollup's [tree-shaking guide](https://rollupjs.org/introduction/#tree-shaking).
329
+
[^export-conditions]: Conditional exports are a Node.js feature that allows packages to define different entry points based on environment or import method. See [Conditional exports](https://nodejs.org/api/packages.html#conditional-exports) in the Node.js documentation.
330
+
331
+
[^dce]: Dead code elimination is a standard compiler optimization. See esbuild's documentation on [tree shaking](https://esbuild.github.io/api/#tree-shaking), Rollup's [tree-shaking guide](https://rollupjs.org/introduction/#tree-shaking) and Rich Harris's article on [dead code elimination](https://medium.com/@Rich_Harris/tree-shaking-versus-dead-code-elimination-d3765df85c80).
0 commit comments