Răsfoiți Sursa

fix(runtime-core): handle patch stable fragment edge case (#12411)

close #12410
linzhe 6 luni în urmă
părinte
comite
94aeb64ccd

+ 59 - 1
packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts

@@ -29,7 +29,7 @@ import {
   setBlockTracking,
   withCtx,
 } from '@vue/runtime-test'
-import { PatchFlags, SlotFlags } from '@vue/shared'
+import { PatchFlags, SlotFlags, toDisplayString } from '@vue/shared'
 import { SuspenseImpl } from '../src/components/Suspense'
 
 describe('renderer: optimized mode', () => {
@@ -1402,4 +1402,62 @@ describe('renderer: optimized mode', () => {
     expect(inner(root)).toBe('<!--v-if-->')
     expect(beforeUnmountSpy).toHaveBeenCalledTimes(1)
   })
+
+  // #12411
+  test('handle patch stable fragment with non-reactive v-for source', async () => {
+    const count = ref(0)
+    const foo: any = []
+    function updateFoo() {
+      for (let n = 0; n < 3; n++) {
+        foo[n] = n + 1 + '_foo'
+      }
+    }
+    const Comp = {
+      setup() {
+        return () => {
+          // <div>{{ count }}</div>
+          // <div v-for='item in foo'>{{ item }}</div>
+          return (
+            openBlock(),
+            createElementBlock(
+              Fragment,
+              null,
+              [
+                createElementVNode(
+                  'div',
+                  null,
+                  toDisplayString(count.value),
+                  PatchFlags.TEXT,
+                ),
+                (openBlock(),
+                createElementBlock(
+                  Fragment,
+                  null,
+                  renderList(foo, item => {
+                    return createElementVNode(
+                      'div',
+                      null,
+                      toDisplayString(item),
+                      PatchFlags.TEXT,
+                    )
+                  }),
+                  PatchFlags.STABLE_FRAGMENT,
+                )),
+              ],
+              PatchFlags.STABLE_FRAGMENT,
+            )
+          )
+        }
+      },
+    }
+
+    render(h(Comp), root)
+    expect(inner(root)).toBe('<div>0</div>')
+    updateFoo()
+    count.value++
+    await nextTick()
+    expect(inner(root)).toBe(
+      '<div>1</div><div>1_foo</div><div>2_foo</div><div>3_foo</div>',
+    )
+  })
 })

+ 2 - 1
packages/runtime-core/src/renderer.ts

@@ -1115,7 +1115,8 @@ function baseCreateRenderer(
         dynamicChildren &&
         // #2715 the previous fragment could've been a BAILed one as a result
         // of renderSlot() with no valid children
-        n1.dynamicChildren
+        n1.dynamicChildren &&
+        n1.dynamicChildren.length === dynamicChildren.length
       ) {
         // a stable fragment (template root or <template v-for>) doesn't need to
         // patch children order, but it may contain dynamicChildren.