Skip to content

Conversation

@linzhe141
Copy link
Contributor

@linzhe141 linzhe141 commented Nov 16, 2024

close #12410

Summary by CodeRabbit

  • Tests

    • Added test coverage for list rendering with non-reactive sources in fragments.
  • Bug Fixes

    • Improved rendering optimization logic for fragments to more accurately determine when to apply fast-path rendering, ensuring better performance and correctness in dynamic rendering scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions
Copy link

github-actions bot commented Nov 16, 2024

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 103 kB (+37 B) 38.9 kB (+10 B) 35 kB (-35 B)
vue.global.prod.js 161 kB (+37 B) 58.8 kB (+9 B) 52.4 kB (+27 B)

Usages

Name Size Gzip Brotli
createApp (CAPI only) 46.9 kB (+37 B) 18.3 kB (+7 B) 16.8 kB (+8 B)
createApp 55.1 kB (+37 B) 21.4 kB (+8 B) 19.6 kB (-4 B)
createSSRApp 59.3 kB (+37 B) 23.2 kB (+8 B) 21.1 kB (+11 B)
defineCustomElement 60.7 kB (+37 B) 23.1 kB (+11 B) 21.1 kB (+7 B)
overall 69.4 kB (+37 B) 26.6 kB (+11 B) 24.2 kB (-45 B)

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 16, 2024

Open in StackBlitz

@vue/compiler-core

npm i https://pkg.pr.new/@vue/compiler-core@12411

@vue/compiler-dom

npm i https://pkg.pr.new/@vue/compiler-dom@12411

@vue/compiler-sfc

npm i https://pkg.pr.new/@vue/compiler-sfc@12411

@vue/compiler-ssr

npm i https://pkg.pr.new/@vue/compiler-ssr@12411

@vue/reactivity

npm i https://pkg.pr.new/@vue/reactivity@12411

@vue/runtime-core

npm i https://pkg.pr.new/@vue/runtime-core@12411

@vue/runtime-dom

npm i https://pkg.pr.new/@vue/runtime-dom@12411

@vue/server-renderer

npm i https://pkg.pr.new/@vue/server-renderer@12411

@vue/shared

npm i https://pkg.pr.new/@vue/shared@12411

vue

npm i https://pkg.pr.new/vue@12411

@vue/compat

npm i https://pkg.pr.new/@vue/compat@12411

commit: 15eb349

@edison1105 edison1105 changed the title fix: should check if it is STABLE_FRAGMENT at runtime fix(runtime-core): handle patch stable fragment edge case Nov 18, 2024
@edison1105 edison1105 added ready to merge The PR is ready to be merged. 🔩 p2-edge-case 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. and removed 🔩 p2-edge-case labels Nov 18, 2024
@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Walkthrough

This PR addresses a bug where non-reactive arrays conditionally displayed based on reactive values cause rendering errors. It adds a length check to the fragment patching logic in the renderer to ensure dynamic children can be safely optimized, and includes a test case validating the fix.

Changes

Cohort / File(s) Change Summary
Test Addition
packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts
Added import of toDisplayString from @vue/shared. Added new test case "handle patch stable fragment with non-reactive v-for source" to validate fragment rendering with non-reactive array sources and reactive state updates.
Renderer Optimization Guard
packages/runtime-core/src/renderer.ts
Enhanced the fragment dynamic children patching condition in baseCreateRenderer to check both that n1.dynamicChildren exists and that its length matches the current dynamicChildren length. Falls back to patchChildren if the guard fails, preventing unsafe optimization attempts.

Sequence Diagram

sequenceDiagram
    participant Renderer
    participant Fragment Patch Logic
    participant Fast Path (patchBlockChildren)
    participant Fallback Path (patchChildren)

    Renderer->>Fragment Patch Logic: Process fragment update
    Fragment Patch Logic->>Fragment Patch Logic: Check n1.dynamicChildren exists?
    Fragment Patch Logic->>Fragment Patch Logic: Check length matches?
    
    alt Both conditions true
        Fragment Patch Logic->>Fast Path (patchBlockChildren): Use optimized path
        Fast Path (patchBlockChildren)->>Renderer: Efficient patch applied
    else Condition fails
        Fragment Patch Logic->>Fallback Path (patchChildren): Use standard path
        Fallback Path (patchChildren)->>Renderer: Full patch applied
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • The test addition is straightforward and self-contained, validating a specific scenario
  • The renderer change is a minimal, localized logic enhancement with clear intent
  • The length check is a defensive guard that prevents a specific class of patching errors

Areas requiring attention:

  • Verify that the length check doesn't inadvertently skip legitimate optimization opportunities
  • Confirm the test case comprehensively covers the non-reactive v-for source scenario

Possibly related PRs

  • PR #13306: Modifies similar fragment and block children patching logic in runtime-core renderer, addressing related optimization path decisions.

Poem

🐰 A rabbit hops through fragment trees,
With dynamic children dancing free,
Length-checked and safe, no more dismay,
When non-reactive arrays come to play! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main fix: handling a stable fragment edge case in the renderer, which directly addresses the bug in the linked issue.
Linked Issues check ✅ Passed The code changes add a length-aware guard to prevent incorrect fast-path execution when patching stable fragments with mismatched dynamic children, directly fixing the 'oldVNode is undefined' error reported in issue #12410.
Out of Scope Changes check ✅ Passed All changes are focused on fixing the stable fragment patching logic and include a test case reproducing the exact scenario from the linked issue, with no extraneous modifications.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f82f23 and 15eb349.

📒 Files selected for processing (2)
  • packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts (2 hunks)
  • packages/runtime-core/src/renderer.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts (1)
packages/runtime-core/src/vnode.ts (2)
  • createElementBlock (340-359)
  • Fragment (63-68)
🔇 Additional comments (3)
packages/runtime-core/src/renderer.ts (1)

1098-1099: LGTM! Critical fix for stable fragment patching.

These guards correctly prevent the "oldVNode is undefined" error by ensuring:

  1. n1.dynamicChildren exists before accessing it in patchBlockChildren
  2. The structure is truly stable (matching lengths) before taking the optimized path

When non-reactive data is conditionally rendered, the initial render may have no dynamicChildren or a different count, causing a mismatch. This fix appropriately falls back to patchChildren for a full diff in such cases.

packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts (2)

32-32: LGTM! Required import for template stringification.

The toDisplayString import is needed for the new test case to format values within template fragments, matching Vue's compiler-generated code for template interpolation.


1405-1462: LGTM! Excellent regression test for the edge case.

This test effectively reproduces the bug scenario from issue #12410:

  • Combines reactive state (count) with a non-reactive array (foo)
  • Non-reactive mutation of foo followed by reactive update triggers the edge case
  • Old vnode has no dynamic children (empty foo), new vnode has 3
  • Without the fix at lines 1098-1099, this would cause "oldVNode is undefined" in patchBlockChildren
  • With the fix, it correctly falls back to patchChildren for a full diff

The test validates both the initial render and the problematic update path, ensuring the fix prevents the error while maintaining correct DOM output.


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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. ready to merge The PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"oldVNode is undefined" when combining reactive and non-reactive values in template's condition

2 participants