Преглед изворни кода

fix(transition): fix broken leave transition on dev root fragment (#5268)

edison пре 4 година
родитељ
комит
767d212d20
2 измењених фајлова са 88 додато и 4 уклоњено
  1. 17 1
      packages/runtime-core/src/renderer.ts
  2. 71 3
      packages/vue/__tests__/Transition.spec.ts

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

@@ -2159,7 +2159,23 @@ function baseCreateRenderer(
   const remove: RemoveFn = vnode => {
     const { type, el, anchor, transition } = vnode
     if (type === Fragment) {
-      removeFragment(el!, anchor!)
+      if (
+        __DEV__ &&
+        vnode.patchFlag > 0 &&
+        vnode.patchFlag & PatchFlags.DEV_ROOT_FRAGMENT &&
+        transition &&
+        !transition.persisted
+      ) {
+        ;(vnode.children as VNode[]).forEach(child => {
+          if (child.type === Comment) {
+            hostRemove(child.el!)
+          } else {
+            remove(child)
+          }
+        })
+      } else {
+        removeFragment(el!, anchor!)
+      }
       return
     }
 

+ 71 - 3
packages/vue/__tests__/Transition.spec.ts

@@ -1979,9 +1979,7 @@ describe('e2e: Transition', () => {
         </div>
       `
     }).mount(document.createElement('div'))
-    expect(
-      `invalid <transition> mode: none`
-    ).toHaveBeenWarned()
+    expect(`invalid <transition> mode: none`).toHaveBeenWarned()
   })
 
   // #3227
@@ -2023,4 +2021,74 @@ describe('e2e: Transition', () => {
     expect(outerSpy).toHaveBeenCalledTimes(1)
     expect(root.innerHTML).toBe(`<!---->`)
   })
+
+  test(
+    'should work with dev root fragment',
+    async () => {
+      await page().evaluate(() => {
+        const { createApp, ref } = (window as any).Vue
+        createApp({
+          components: {
+            Comp: {
+              template: `
+                  <!-- Broken! -->
+                  <div><slot /></div>
+                `
+            }
+          },
+          template: `
+            <div id="container">
+              <transition>
+                <Comp class="test" v-if="toggle">      
+                  <div>content</div>
+                </Comp>
+              </transition>
+            </div>
+            <button id="toggleBtn" @click="click">button</button>
+          `,
+          setup: () => {
+            const toggle = ref(true)
+            const click = () => (toggle.value = !toggle.value)
+            return { toggle, click }
+          }
+        }).mount('#app')
+      })
+      expect(await html('#container')).toBe(
+        '<!-- Broken! --><div class="test"><div>content</div></div>'
+      )
+
+      // leave
+      expect(await classWhenTransitionStart()).toStrictEqual([
+        'test',
+        'v-leave-from',
+        'v-leave-active'
+      ])
+      await nextFrame()
+      expect(await classList('.test')).toStrictEqual([
+        'test',
+        'v-leave-active',
+        'v-leave-to'
+      ])
+      await transitionFinish()
+      expect(await html('#container')).toBe('<!--v-if-->')
+
+      // enter
+      expect(await classWhenTransitionStart()).toStrictEqual([
+        'test',
+        'v-enter-from',
+        'v-enter-active'
+      ])
+      await nextFrame()
+      expect(await classList('.test')).toStrictEqual([
+        'test',
+        'v-enter-active',
+        'v-enter-to'
+      ])
+      await transitionFinish()
+      expect(await html('#container')).toBe(
+        '<!-- Broken! --><div class="test"><div>content</div></div>'
+      )
+    },
+    E2E_TIMEOUT
+  )
 })