diff --git a/packages/runtime-dom/src/components/Transition.ts b/packages/runtime-dom/src/components/Transition.ts
index 50c03bf0ad1..6c6344bfcac 100644
--- a/packages/runtime-dom/src/components/Transition.ts
+++ b/packages/runtime-dom/src/components/Transition.ts
@@ -259,11 +259,11 @@ export function resolveTransitionProps(
// add *-leave-active class before reflow so in the case of a cancelled enter transition
// the css will not get the final state (#10677)
if (!el._enterCancelled) {
+ // force reflow so *-leave-from classes immediately take effect (#2593)
forceReflow()
addTransitionClass(el, leaveActiveClass)
} else {
addTransitionClass(el, leaveActiveClass)
- // force reflow so *-leave-from classes immediately take effect (#2593)
forceReflow()
}
nextFrame(() => {
diff --git a/packages/vue/__tests__/e2e/Transition.spec.ts b/packages/vue/__tests__/e2e/Transition.spec.ts
index c0863a75991..46e73e3af6a 100644
--- a/packages/vue/__tests__/e2e/Transition.spec.ts
+++ b/packages/vue/__tests__/e2e/Transition.spec.ts
@@ -3,7 +3,7 @@ import path from 'node:path'
import { Transition, createApp, h, nextTick, ref } from 'vue'
describe('e2e: Transition', () => {
- const { page, html, classList, isVisible, timeout, nextFrame, click } =
+ const { page, html, classList, style, isVisible, timeout, nextFrame, click } =
setupPuppeteer()
const baseUrl = `file://${path.resolve(__dirname, './transition.html')}`
@@ -2986,6 +2986,56 @@ describe('e2e: Transition', () => {
)
})
+ test('reflow after *-leave-from before *-leave-active', async () => {
+ await page().evaluate(() => {
+ const { createApp, ref } = (window as any).Vue
+ createApp({
+ template: `
+
+
+ `,
+ setup: () => {
+ const toggle = ref(false)
+ const click = () => (toggle.value = !toggle.value)
+ return {
+ toggle,
+ click,
+ }
+ },
+ }).mount('#app')
+ })
+
+ // if transition starts while there's v-leave-active added along with v-leave-from, its bad, it has to start when it doesnt have the v-leave-from
+
+ // enter
+ await classWhenTransitionStart()
+ await transitionFinish()
+
+ // leave
+ expect(await classWhenTransitionStart()).toStrictEqual([
+ 'test-reflow',
+ 'test-reflow-leave-from',
+ 'test-reflow-leave-active',
+ ])
+
+ console.log(await style('.test-reflow', 'opacity'))
+ expect(await style('.test-reflow', 'opacity')).toStrictEqual('0.9')
+
+ await nextFrame()
+ expect(await classList('.test-reflow')).toStrictEqual([
+ 'test-reflow',
+ 'test-reflow-leave-active',
+ 'test-reflow-leave-to',
+ ])
+
+ await transitionFinish()
+ expect(await html('#container')).toBe('')
+ })
+
test('warn when used on multiple elements', async () => {
createApp({
render() {
diff --git a/packages/vue/__tests__/e2e/e2eUtils.ts b/packages/vue/__tests__/e2e/e2eUtils.ts
index 8512dcaae47..8a63e0a3530 100644
--- a/packages/vue/__tests__/e2e/e2eUtils.ts
+++ b/packages/vue/__tests__/e2e/e2eUtils.ts
@@ -39,6 +39,7 @@ interface PuppeteerUtils {
value(selector: string): Promise
html(selector: string): Promise
classList(selector: string): Promise
+ style(selector: string, property: keyof CSSStyleDeclaration): Promise
children(selector: string): Promise
isVisible(selector: string): Promise
isChecked(selector: string): Promise
@@ -120,6 +121,19 @@ export function setupPuppeteer(args?: string[]): PuppeteerUtils {
return page.$eval(selector, (node: any) => [...node.children])
}
+ async function style(
+ selector: string,
+ property: keyof CSSStyleDeclaration,
+ ): Promise {
+ return await page.$eval(
+ selector,
+ (node, property) => {
+ return window.getComputedStyle(node)[property]
+ },
+ property,
+ )
+ }
+
async function isVisible(selector: string): Promise {
const display = await page.$eval(selector, node => {
return window.getComputedStyle(node).display
@@ -195,6 +209,7 @@ export function setupPuppeteer(args?: string[]): PuppeteerUtils {
value,
html,
classList,
+ style,
children,
isVisible,
isChecked,
diff --git a/packages/vue/__tests__/e2e/transition.html b/packages/vue/__tests__/e2e/transition.html
index c44da2f78a1..ab404d67dc7 100644
--- a/packages/vue/__tests__/e2e/transition.html
+++ b/packages/vue/__tests__/e2e/transition.html
@@ -16,11 +16,21 @@
.test-appear,
.test-enter,
.test-leave-active,
+ .test-reflow-enter,
+ .test-reflow-leave-to,
.hello,
.bye.active,
.changed-enter {
opacity: 0;
}
+ .test-reflow-leave-active,
+ .test-reflow-enter-active {
+ -webkit-transition: opacity 50ms ease;
+ transition: opacity 50ms ease;
+ }
+ .test-reflow-leave-from {
+ opacity: 0.9;
+ }
.test-anim-enter-active {
animation: test-enter 50ms;
-webkit-animation: test-enter 50ms;