]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(TransitionGroup): avoid set transition hooks for comment nodes and text nodes...
authoredison <daiwei521@126.com>
Sun, 14 Apr 2024 15:16:01 +0000 (23:16 +0800)
committerGitHub <noreply@github.com>
Sun, 14 Apr 2024 15:16:01 +0000 (23:16 +0800)
close #4621
close #4622
close #5153
close #5168
close #7898
close #9067

packages/runtime-dom/src/components/TransitionGroup.ts
packages/vue/__tests__/e2e/TransitionGroup.spec.ts

index f98e82b2734ba85bd54d2c377db11ab5a354d0d2..763b7a98b240865d60139b819f7f832846e7321d 100644 (file)
@@ -112,7 +112,29 @@ const TransitionGroupImpl: ComponentOptions = {
         tag = 'span'
       }
 
-      prevChildren = children
+      prevChildren = []
+      if (children) {
+        for (let i = 0; i < children.length; i++) {
+          const child = children[i]
+          if (child.el && child.el instanceof Element) {
+            prevChildren.push(child)
+            setTransitionHooks(
+              child,
+              resolveTransitionHooks(
+                child,
+                cssTransitionProps,
+                state,
+                instance,
+              ),
+            )
+            positionMap.set(
+              child,
+              (child.el as Element).getBoundingClientRect(),
+            )
+          }
+        }
+      }
+
       children = slots.default ? getTransitionRawChildren(slots.default()) : []
 
       for (let i = 0; i < children.length; i++) {
@@ -127,17 +149,6 @@ const TransitionGroupImpl: ComponentOptions = {
         }
       }
 
-      if (prevChildren) {
-        for (let i = 0; i < prevChildren.length; i++) {
-          const child = prevChildren[i]
-          setTransitionHooks(
-            child,
-            resolveTransitionHooks(child, cssTransitionProps, state, instance),
-          )
-          positionMap.set(child, (child.el as Element).getBoundingClientRect())
-        }
-      }
-
       return createVNode(tag, null, children)
     }
   },
index febc9d3c20ab0c306ba904c20829d790625580ee..da3f4a42de9026efb502f81ae47af5715023476b 100644 (file)
@@ -508,4 +508,126 @@ describe('e2e: TransitionGroup', () => {
 
     expect(`<TransitionGroup> children must be keyed`).toHaveBeenWarned()
   })
+
+  // #5168, #7898, #9067
+  test(
+    'avoid set transition hooks for comment node',
+    async () => {
+      await page().evaluate(duration => {
+        const { createApp, ref, h, createCommentVNode } = (window as any).Vue
+
+        const show = ref(false)
+        createApp({
+          template: `
+            <div id="container">
+              <transition-group name="test">
+                <div v-for="item in items" :key="item" class="test">{{item}}</div>
+                <Child key="child"/>
+              </transition-group>
+            </div>
+            <button id="toggleBtn" @click="click">button</button>
+          `,
+          components: {
+            Child: {
+              setup() {
+                return () =>
+                  show.value
+                    ? h('div', { class: 'test' }, 'child')
+                    : createCommentVNode('v-if', true)
+              },
+            },
+          },
+          setup: () => {
+            const items = ref([])
+            const click = () => {
+              items.value = ['a', 'b', 'c']
+              setTimeout(() => {
+                show.value = true
+              }, duration)
+            }
+            return { click, items }
+          },
+        }).mount('#app')
+      }, duration)
+
+      expect(await html('#container')).toBe(`<!--v-if-->`)
+
+      expect(await htmlWhenTransitionStart()).toBe(
+        `<div class="test test-enter-from test-enter-active">a</div>` +
+          `<div class="test test-enter-from test-enter-active">b</div>` +
+          `<div class="test test-enter-from test-enter-active">c</div>` +
+          `<!--v-if-->`,
+      )
+
+      await transitionFinish(duration)
+      await nextFrame()
+      expect(await html('#container')).toBe(
+        `<div class="test">a</div>` +
+          `<div class="test">b</div>` +
+          `<div class="test">c</div>` +
+          `<div class="test test-enter-active test-enter-to">child</div>`,
+      )
+
+      await transitionFinish(duration)
+      expect(await html('#container')).toBe(
+        `<div class="test">a</div>` +
+          `<div class="test">b</div>` +
+          `<div class="test">c</div>` +
+          `<div class="test">child</div>`,
+      )
+    },
+    E2E_TIMEOUT,
+  )
+
+  // #4621, #4622, #5153
+  test(
+    'avoid set transition hooks for text node',
+    async () => {
+      await page().evaluate(() => {
+        const { createApp, ref } = (window as any).Vue
+        const app = createApp({
+          template: `
+            <div id="container">
+              <transition-group name="test">
+                <div class="test">foo</div>
+                <div class="test" v-if="show">bar</div>
+              </transition-group>
+            </div>
+            <button id="toggleBtn" @click="click">button</button>
+          `,
+          setup: () => {
+            const show = ref(false)
+            const click = () => {
+              show.value = true
+            }
+            return { show, click }
+          },
+        })
+
+        app.config.compilerOptions.whitespace = 'preserve'
+        app.mount('#app')
+      })
+
+      expect(await html('#container')).toBe(`<div class="test">foo</div>` + ` `)
+
+      expect(await htmlWhenTransitionStart()).toBe(
+        `<div class="test">foo</div>` +
+          ` ` +
+          `<div class="test test-enter-from test-enter-active">bar</div>`,
+      )
+
+      await nextFrame()
+      expect(await html('#container')).toBe(
+        `<div class="test">foo</div>` +
+          ` ` +
+          `<div class="test test-enter-active test-enter-to">bar</div>`,
+      )
+
+      await transitionFinish(duration)
+      expect(await html('#container')).toBe(
+        `<div class="test">foo</div>` + ` ` + `<div class="test">bar</div>`,
+      )
+    },
+    E2E_TIMEOUT,
+  )
 })