Skip to content

perf(server-renderer): avoid materializing iterables in ssrRenderList#14821

Open
dennybiasiolli wants to merge 1 commit into
vuejs:mainfrom
dennybiasiolli:fix-ssr-render-list
Open

perf(server-renderer): avoid materializing iterables in ssrRenderList#14821
dennybiasiolli wants to merge 1 commit into
vuejs:mainfrom
dennybiasiolli:fix-ssr-render-list

Conversation

@dennybiasiolli
Copy link
Copy Markdown

@dennybiasiolli dennybiasiolli commented May 14, 2026

Summary

This PR removes an unnecessary intermediate array allocation in ssrRenderList when iterating over iterable sources (generators, Set, Map, custom iterables).

Problem

ssrRenderList handles v-for iteration during SSR. When the source is an object implementing Symbol.iterator, the current code calls Array.from(source) to eagerly materialize the entire iterable into an in-memory array before rendering any items:

// Before (packages/server-renderer/src/helpers/ssrRenderList.ts)
if (source[Symbol.iterator as any]) {
  const arr = Array.from(source as Iterable<any>) // <-- problem here
  for (let i = 0, l = arr.length; i < l; i++) {
    renderItem(arr[i], i)
  }
}

Since ssrRenderList is void (it calls renderItem for side effects, not to collect results), the intermediate array is completely unnecessary. For large iterables (e.g., a generator yielding millions of items from a database cursor), this causes:

  • O(n) extra memory for the intermediate array, on top of whatever the renderItem callback produces.
  • A blocking materialization phase before any rendering can begin.
  • Potential memory exhaustion or hangs for very large or infinite iterables.

Solution

Replace Array.from(source) with a for...of loop that lazily iterates the iterable and calls renderItem for each item as it is produced:

// After
if (source[Symbol.iterator as any]) {
  let i = 0
  for (const item of source as Iterable<any>) {
    renderItem(item, i++)
  }
}

This reduces the memory overhead of the rendering loop from O(n) to O(1), while producing identical output.

Why the client-side renderList is not changed

The client-side renderList (packages/runtime-core/src/helpers/renderList.ts) already uses the two-argument form Array.from(source, mapFn), which maps during iteration in a single pass without creating an intermediate array. Since it must return a VNodeChild[], it needs the result array regardless — there is no unnecessary allocation to eliminate.

Test Plan

  • All 294 existing server-renderer tests pass without modification.
  • Added 3 new test cases in packages/server-renderer/__tests__/ssrRenderList.spec.ts:
    • Set iteration — verifies items and indices are correct for Set sources.
    • Map iteration — verifies items and indices are correct for Map sources.
    • Lazy iteration verification — uses a generator with side effects to confirm that items are yielded and rendered one at a time (not materialized upfront).

Co-authored-by: Grok

Summary by CodeRabbit

Release Notes

  • Performance Improvements
    • Server-side rendering now efficiently handles Sets, Maps, and generator functions through direct iteration
    • Reduced memory usage by eliminating unnecessary array conversions for iterables
    • Enabled lazy evaluation of generators without intermediate buffering

Review Change Stack

Replace `Array.from(source)` with a `for...of` loop when iterating
iterable sources in `ssrRenderList`. The previous code eagerly
materialized the entire iterable into an intermediate array before
rendering, which is unnecessary since `ssrRenderList` is void and
does not need to collect results.

The new approach iterates lazily, reducing memory overhead from O(n)
to O(1) for the rendering loop itself. This avoids memory spikes when
iterating large generators or custom iterables during SSR.

Co-authored-by: Grok
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

📝 Walkthrough

Walkthrough

ssrRenderList is updated to iterate iterables directly using for...of instead of materializing them with Array.from, enabling lazy evaluation. Test coverage is added for Set, Map, and generator-based iterables to validate correct rendering and confirm lazy iteration behavior.

Changes

Lazy Iterable Rendering

Layer / File(s) Summary
Direct iteration implementation
packages/server-renderer/src/helpers/ssrRenderList.ts
ssrRenderList replaces Array.from materialization with a direct for...of loop and explicit index counter, eliminating intermediate array buffering for iterable sources.
Iterable type coverage and laziness tests
packages/server-renderer/__tests__/ssrRenderList.spec.ts
Tests validate Set and Map rendering, and a generator test confirms lazy iteration: values are yielded and rendered in sequence without buffering all values upfront.

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

ready to merge, scope: ssr

Poem

🐰 A list rendered swift and lean,
No arrays buffered in between,
for...of yields with lazy grace,
Each item rendered in its place,
Sets and Maps now find their space!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and specifically describes the main optimization: avoiding materialization of iterables in ssrRenderList by switching from Array.from to lazy for...of iteration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@edison1105 edison1105 added scope: ssr ready to merge The PR is ready to be merged. labels May 15, 2026
@github-actions
Copy link
Copy Markdown

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 106 kB 40 kB 36 kB
vue.global.prod.js 164 kB 60 kB 53.4 kB

Usages

Name Size Gzip Brotli
createApp (CAPI only) 48.7 kB 18.9 kB 17.3 kB
createApp 56.8 kB 22 kB 20.1 kB
createSSRApp 61.1 kB 23.7 kB 21.6 kB
defineCustomElement 63 kB 23.9 kB 21.8 kB
overall 71.6 kB 27.4 kB 25 kB

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 15, 2026

Open in StackBlitz

@vue/compiler-core

pnpm add https://pkg.pr.new/@vue/compiler-core@14821
npm i https://pkg.pr.new/@vue/compiler-core@14821
yarn add https://pkg.pr.new/@vue/compiler-core@14821.tgz

@vue/compiler-dom

pnpm add https://pkg.pr.new/@vue/compiler-dom@14821
npm i https://pkg.pr.new/@vue/compiler-dom@14821
yarn add https://pkg.pr.new/@vue/compiler-dom@14821.tgz

@vue/compiler-sfc

pnpm add https://pkg.pr.new/@vue/compiler-sfc@14821
npm i https://pkg.pr.new/@vue/compiler-sfc@14821
yarn add https://pkg.pr.new/@vue/compiler-sfc@14821.tgz

@vue/compiler-ssr

pnpm add https://pkg.pr.new/@vue/compiler-ssr@14821
npm i https://pkg.pr.new/@vue/compiler-ssr@14821
yarn add https://pkg.pr.new/@vue/compiler-ssr@14821.tgz

@vue/reactivity

pnpm add https://pkg.pr.new/@vue/reactivity@14821
npm i https://pkg.pr.new/@vue/reactivity@14821
yarn add https://pkg.pr.new/@vue/reactivity@14821.tgz

@vue/runtime-core

pnpm add https://pkg.pr.new/@vue/runtime-core@14821
npm i https://pkg.pr.new/@vue/runtime-core@14821
yarn add https://pkg.pr.new/@vue/runtime-core@14821.tgz

@vue/runtime-dom

pnpm add https://pkg.pr.new/@vue/runtime-dom@14821
npm i https://pkg.pr.new/@vue/runtime-dom@14821
yarn add https://pkg.pr.new/@vue/runtime-dom@14821.tgz

@vue/server-renderer

pnpm add https://pkg.pr.new/@vue/server-renderer@14821
npm i https://pkg.pr.new/@vue/server-renderer@14821
yarn add https://pkg.pr.new/@vue/server-renderer@14821.tgz

@vue/shared

pnpm add https://pkg.pr.new/@vue/shared@14821
npm i https://pkg.pr.new/@vue/shared@14821
yarn add https://pkg.pr.new/@vue/shared@14821.tgz

vue

pnpm add https://pkg.pr.new/vue@14821
npm i https://pkg.pr.new/vue@14821
yarn add https://pkg.pr.new/vue@14821.tgz

@vue/compat

pnpm add https://pkg.pr.new/@vue/compat@14821
npm i https://pkg.pr.new/@vue/compat@14821
yarn add https://pkg.pr.new/@vue/compat@14821.tgz

commit: cbd7771

@edison1105 edison1105 changed the title perf(server-renderer): lazy iterate iterables in ssrRenderList perf(server-renderer): avoid materializing iterables in ssrRenderList May 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready to merge The PR is ready to be merged. scope: ssr

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants