]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): fix fragment update inside de-opt slots
authorEvan You <yyx990803@gmail.com>
Fri, 4 Jun 2021 22:03:40 +0000 (18:03 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 4 Jun 2021 22:03:40 +0000 (18:03 -0400)
fix #3881

packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts
packages/runtime-core/src/renderer.ts

index bddd7d1d318e24df82fb62283a3be875a9e0a5f2..59deb5a00e2d3a48156b7214919f81ca838e154a 100644 (file)
@@ -20,7 +20,9 @@ import {
   onBeforeUnmount,
   createTextVNode,
   SetupContext,
-  createApp
+  createApp,
+  FunctionalComponent,
+  renderList
 } from '@vue/runtime-test'
 import { PatchFlags, SlotFlags } from '@vue/shared'
 import { SuspenseImpl } from '../src/components/Suspense'
@@ -821,4 +823,62 @@ describe('renderer: optimized mode', () => {
     await nextTick()
     expect(inner(root)).toBe('<div><div>true</div></div>')
   })
+
+  // #3881
+  // root cause: fragment inside a compiled slot passed to component which
+  // programmatically invokes the slot. The entire slot should de-opt but
+  // the fragment was incorretly put in optimized mode which causes it to skip
+  // updates for its inner components.
+  test('fragments inside programmatically invoked compiled slot should de-opt properly', async () => {
+    const Parent: FunctionalComponent = (_, { slots }) => slots.default!()
+    const Dummy = () => 'dummy'
+
+    const toggle = ref(true)
+    const force = ref(0)
+
+    const app = createApp({
+      render() {
+        if (!toggle.value) {
+          return null
+        }
+        return h(
+          Parent,
+          { n: force.value },
+          {
+            default: withCtx(
+              () => [
+                createVNode('ul', null, [
+                  (openBlock(),
+                  createBlock(
+                    Fragment,
+                    null,
+                    renderList(1, item => {
+                      return createVNode('li', null, [createVNode(Dummy)])
+                    }),
+                    64 /* STABLE_FRAGMENT */
+                  ))
+                ])
+              ],
+              undefined,
+              true
+            ),
+            _: 1 /* STABLE */
+          }
+        )
+      }
+    })
+
+    app.mount(root)
+
+    // force a patch
+    force.value++
+    await nextTick()
+    expect(inner(root)).toBe(`<ul><li>dummy</li></ul>`)
+
+    // unmount
+    toggle.value = false
+    await nextTick()
+    // should successfully unmount without error
+    expect(inner(root)).toBe(`<!---->`)
+  })
 })
index e190669951c28df4020f5aa598da1fddb4d4b4d4..c7549ae15987e2f0a467f806f41adff4ee5bbc58 100644 (file)
@@ -1170,7 +1170,7 @@ function baseCreateRenderer(
     const fragmentEndAnchor = (n2.anchor = n1 ? n1.anchor : hostCreateText(''))!
 
     let { patchFlag, dynamicChildren, slotScopeIds: fragmentSlotScopeIds } = n2
-    if (patchFlag > 0) {
+    if (dynamicChildren) {
       optimized = true
     }