|
1 | 1 | # @tenphi/tasty |
2 | 2 |
|
| 3 | +## 3.4.0 |
| 4 | + |
| 5 | +### Minor Changes |
| 6 | + |
| 7 | +- [#275](https://github.com/tenphi/tasty/pull/275) [`05ad7a8`](https://github.com/tenphi/tasty/commit/05ad7a86a90165474bd2ba86f79b50bdbf8d9b8e) Thanks [@tenphi](https://github.com/tenphi)! - Cut per-render styling overhead: faster LRU reads everywhere, and memoized |
| 8 | + chunk cache keys on the server. |
| 9 | + |
| 10 | + **LRU reads no longer pay three extra hash lookups** |
| 11 | + |
| 12 | + The LRU list stored keys rather than node references, so every `get()` of a |
| 13 | + non-most-recent entry did `map.get()` three times — previous, next and the |
| 14 | + current head — purely to rewire pointers. In a production trace of a real app |
| 15 | + that showed up as 12.9 ms across `touch` / `get` / `set`. |
| 16 | + |
| 17 | + The list now holds nodes directly, which makes rewiring pure pointer work, and |
| 18 | + a `get()` that hits the most-recent entry returns after a single lookup. |
| 19 | + Eviction, `onEvict`, iteration order of `keys()` and every other observable |
| 20 | + behaviour are unchanged. All ten caches benefit — `pipelineCache`, |
| 21 | + `conditionCache`, `parseCache`, `simplifyCache`, the parser cache, |
| 22 | + `cacheWrapper` and the colour caches. A read that has to rewire the list, which |
| 23 | + is the steady-state shape, measures 1.79x faster. |
| 24 | + |
| 25 | + **`computeStyles({ stableStyles: true })` memoizes chunk cache keys** |
| 26 | + |
| 27 | + `generateChunkCacheKey()` runs once per chunk per render and stable-stringifies |
| 28 | + every style value in the chunk before any cache is consulted. On the client |
| 29 | + that mostly does not matter, because a component whose styles never change is |
| 30 | + short-circuited by a factory-level class-name cache. On the server that cache |
| 31 | + is deliberately skipped — `computeStyles()` is what feeds the per-request SSR |
| 32 | + collector and what produces the RSC inline `<style>`, so short-circuiting it |
| 33 | + would return a class name for CSS that was never emitted for that request. |
| 34 | + Every server render therefore re-derives the same keys from the same object. |
| 35 | + |
| 36 | + `stableStyles` marks a styles object that outlives the call and will be passed |
| 37 | + in again — a `tasty()` factory's own styles rather than a per-render merge. The |
| 38 | + generated keys are then memoized on that object. `tasty()` sets it |
| 39 | + automatically; there is nothing to configure. A repeat SSR render of one |
| 40 | + component measures **1.8x faster end to end**, not just in key generation. |
| 41 | + |
| 42 | + The flag gates only the write. Reading an existing entry is free and |
| 43 | + unconditional, so nothing has to opt in to benefit from what already ran, and |
| 44 | + anything that would be stored without being read back is left out: a per-render |
| 45 | + merged styles object, a styles object rewritten by recipe resolution, and every |
| 46 | + client render — where the factory-level class-name cache answers everything |
| 47 | + after the first, so `computeStyles()` never sees the same object twice. Storing |
| 48 | + an entry costs roughly twice what generating the key does, so a write-only opt |
| 49 | + -in is worse than no memo at all. |
| 50 | + |
| 51 | + A memo hit re-reads every value the key was built from and confirms none of |
| 52 | + them changed, using the same shallow reference comparison `stableStyles`' |
| 53 | + sibling caches already rely on. A memoized key is stale in exactly the cases |
| 54 | + the un-memoized one was already stale in — an object-valued style mutated in |
| 55 | + place — and in no others. `Styles` remains a plain mutable object; nothing new |
| 56 | + is required of callers. |
| 57 | + |
| 58 | + `generateChunkCacheKey()` takes a matching optional fourth argument for direct |
| 59 | + callers. It defaults to the previous behaviour. |
| 60 | + |
3 | 61 | ## 3.3.1 |
4 | 62 |
|
5 | 63 | ### Patch Changes |
|
0 commit comments