Skip to content

perf: avoid O(N^2) accumulator spread in initHiddenPaths - #310

Merged
leezng merged 1 commit into
leezng:devfrom
ibreathebsb:perf/init-hidden-paths-on2
Apr 29, 2026
Merged

leezng merged 1 commit into
leezng:devfrom
ibreathebsb:perf/init-hidden-paths-on2

Conversation

@ibreathebsb

Copy link
Copy Markdown

Problem

initHiddenPaths (Tree/index.tsx) builds the hidden-paths map with a reduce that spreads the accumulator on every iteration:

return originFlatData.value.reduce((acc, item) => {
  ...
  return {
    ...acc,
    [item.path]: 1,
  };
}, {})

For N flat nodes this allocates a fresh object per iteration and copies every previously-added key — O(N²) total, with N(N-1)/2 property copies.

initHiddenPaths is called synchronously inside setup (line 112) when constructing state.hiddenPaths, so this cost lands on the cold-mount path.

For small JSON it's invisible. For large payloads it dominates: on a real-world 1.9MB payload (~10K flat container nodes) this single line blocks the main thread for ~3–4 seconds during initial render. Profiler confirms the time is spent inside the reduce, not in jsonFlatten or DOM work.

This appears to be the same root cause behind the long-standing #138.

Fix

reduce accumulators are private — mutating in place is safe. One-line change from spread to assignment makes it O(N):

acc[item.path] = 1;
return acc;

Behavior is identical. The final as Record<string, 1> cast is preserved.

Measurements

Same 1.9MB payload, same deep=1, same machine:

Before After
initHiddenPaths time ~3.8s ~12ms
Cold mount of <VueJsonPretty> ~4.0s ~50ms

Smaller payloads see no perceptible change (already fast).

Risk

  • No API change.
  • No behavior change — same output object, same key set.
  • No new allocations introduced; one fewer per iteration.
  • Type cast unchanged.

Closes #138 (or contributes substantially toward it).

initHiddenPaths used `{ ...acc, [path]: 1 }` inside reduce, which
rebuilds the accumulator on every iteration. For N flat nodes this is
O(N^2) — copying ~50M properties for ~10K nodes and blocking the main
thread for 3-4s on initial mount with large payloads (related to leezng#138).

Replace the spread with in-place assignment (`acc[path] = 1`). reduce
accumulators are private — mutation here is safe and O(N).

Measured locally on a 1.9MB payload (~10K flat nodes): cold mount 4s -> ~50ms.
@ibreathebsb

Copy link
Copy Markdown
Author

@leezng Can you review this PR please?

@leezng

leezng commented Apr 29, 2026

Copy link
Copy Markdown
Owner

@ibreathebsb Thanks!

@leezng
leezng merged commit a41ceee into leezng:dev Apr 29, 2026
1 check passed
@ibreathebsb
ibreathebsb deleted the perf/init-hidden-paths-on2 branch April 29, 2026 07:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Slow performance when loading large JSONs and toggling collapse/expand

2 participants