diff --git a/packages-private/vapor-e2e-test/__tests__/transition.spec.ts b/packages-private/vapor-e2e-test/__tests__/transition.spec.ts
index 2a9de7e73..0bfc30598 100644
--- a/packages-private/vapor-e2e-test/__tests__/transition.spec.ts
+++ b/packages-private/vapor-e2e-test/__tests__/transition.spec.ts
@@ -1061,23 +1061,103 @@ describe('vapor transition', () => {
E2E_TIMEOUT,
)
- test.todo(
+ test(
'transition on appear with v-show',
async () => {
const btnSelector = '.show-appear > button'
const containerSelector = '.show-appear > div'
const childSelector = `${containerSelector} > div`
+
+ let calls = await page().evaluate(() => {
+ return (window as any).getCalls('showAppear')
+ })
+ expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
+
+ // appear
+ expect(await classList(childSelector)).contains('test-appear-active')
+
+ await transitionFinish()
+ expect(await html(containerSelector)).toBe(
+ '
content
',
+ )
+ calls = await page().evaluate(() => {
+ return (window as any).getCalls('showAppear')
+ })
+ expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
+
+ // leave
+ expect(
+ (await transitionStart(btnSelector, childSelector)).classNames,
+ ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
+ await nextFrame()
+ expect(await classList(childSelector)).toStrictEqual([
+ 'test',
+ 'test-leave-active',
+ 'test-leave-to',
+ ])
+ await transitionFinish()
+ expect(await isVisible(childSelector)).toBe(false)
+
+ // enter
+ expect(
+ (await transitionStart(btnSelector, childSelector)).classNames,
+ ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
+ await nextFrame()
+ expect(await classList(childSelector)).toStrictEqual([
+ 'test',
+ 'test-enter-active',
+ 'test-enter-to',
+ ])
+ await transitionFinish()
+ expect(await html(containerSelector)).toBe(
+ 'content
',
+ )
},
E2E_TIMEOUT,
)
- test.todo(
+ test(
'transition events should not call onEnter with v-show false',
- async () => {},
+ async () => {
+ const btnSelector = '.show-appear-not-enter > button'
+ const containerSelector = '.show-appear-not-enter > div'
+ const childSelector = `${containerSelector} > div`
+
+ expect(await isVisible(childSelector)).toBe(false)
+ let calls = await page().evaluate(() => {
+ return (window as any).getCalls('notEnter')
+ })
+ expect(calls).toStrictEqual([])
+
+ // enter
+ expect(
+ (await transitionStart(btnSelector, childSelector)).classNames,
+ ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
+ calls = await page().evaluate(() => {
+ return (window as any).getCalls('notEnter')
+ })
+ expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
+ await nextFrame()
+ expect(await classList(childSelector)).toStrictEqual([
+ 'test',
+ 'test-enter-active',
+ 'test-enter-to',
+ ])
+ calls = await page().evaluate(() => {
+ return (window as any).getCalls('notEnter')
+ })
+ expect(calls).not.contain('afterEnter')
+ await transitionFinish()
+ expect(await html(containerSelector)).toBe(
+ 'content
',
+ )
+ calls = await page().evaluate(() => {
+ return (window as any).getCalls('notEnter')
+ })
+ expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
+ },
E2E_TIMEOUT,
)
-
- test.todo('transition on appear with v-show', async () => {}, E2E_TIMEOUT)
})
describe('explicit durations', () => {
diff --git a/packages-private/vapor-e2e-test/transition/App.vue b/packages-private/vapor-e2e-test/transition/App.vue
index a5b32a621..485509824 100644
--- a/packages-private/vapor-e2e-test/transition/App.vue
+++ b/packages-private/vapor-e2e-test/transition/App.vue
@@ -27,6 +27,7 @@ let calls = {
show: [],
showLeaveCancel: [],
showAppear: [],
+ notEnter: [],
}
window.getCalls = key => calls[key]
window.resetCalls = key => (calls[key] = [])
@@ -398,6 +399,20 @@ function changeViewInOut() {
+
+
+
calls.notEnter.push('beforeEnter')"
+ @enter="() => calls.notEnter.push('onEnter')"
+ @afterEnter="() => calls.notEnter.push('afterEnter')"
+ >
+ content
+
+
+
+
diff --git a/packages/compiler-vapor/__tests__/transforms/TransformTransition.spec.ts b/packages/compiler-vapor/__tests__/transforms/TransformTransition.spec.ts
index 7c86b9f37..bcb7b44ce 100644
--- a/packages/compiler-vapor/__tests__/transforms/TransformTransition.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/TransformTransition.spec.ts
@@ -28,6 +28,13 @@ const compileWithElementTransform = makeCompile({
describe('compiler: transition', () => {
test('basic', () => {
+ const { code } = compileWithElementTransform(
+ `foo
`,
+ )
+ expect(code).toMatchSnapshot()
+ })
+
+ test('v-show + appear', () => {
const { code } = compileWithElementTransform(
`foo
`,
)
diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap
index a1de229f5..37ae42ed4 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap
@@ -5,10 +5,7 @@ exports[`compiler: transition > basic 1`] = `
const t0 = _template("foo
")
export function render(_ctx) {
- const n1 = _createComponent(_VaporTransition, {
- appear: () => (""),
- persisted: () => ("")
- }, {
+ const n1 = _createComponent(_VaporTransition, { persisted: () => ("") }, {
"default": () => {
const n0 = t0()
_applyVShow(n0, () => (_ctx.show))
@@ -72,6 +69,27 @@ export function render(_ctx) {
}"
`;
+exports[`compiler: transition > v-show + appear 1`] = `
+"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, createComponent as _createComponent, template as _template } from 'vue';
+const t0 = _template("foo
")
+
+export function render(_ctx) {
+ const lazyApplyVShowFn = []
+ const n1 = _createComponent(_VaporTransition, {
+ appear: () => (""),
+ persisted: () => ("")
+ }, {
+ "default": () => {
+ const n0 = t0()
+ lazyApplyVShowFn.push(() => _applyVShow(n0, () => (_ctx.show)))
+ return n0
+ }
+ }, true)
+ lazyApplyVShowFn.forEach(fn => fn())
+ return n1
+}"
+`;
+
exports[`compiler: transition > work with dynamic keyed children 1`] = `
"import { VaporTransition as _VaporTransition, createKeyedFragment as _createKeyedFragment, createComponent as _createComponent, template as _template } from 'vue';
const t0 = _template("foo
")
diff --git a/packages/compiler-vapor/src/generators/block.ts b/packages/compiler-vapor/src/generators/block.ts
index 654816e09..e7f0610ac 100644
--- a/packages/compiler-vapor/src/generators/block.ts
+++ b/packages/compiler-vapor/src/generators/block.ts
@@ -44,6 +44,10 @@ export function genBlockContent(
const { dynamic, effect, operation, returns, key } = block
const resetBlock = context.enterBlock(block)
+ if (block.hasLazyApplyVShow) {
+ push(NEWLINE, `const lazyApplyVShowFn = []`)
+ }
+
if (root) {
genResolveAssets('component', 'resolveComponent')
genResolveAssets('directive', 'resolveDirective')
@@ -56,6 +60,10 @@ export function genBlockContent(
push(...genOperations(operation, context))
push(...genEffects(effect, context))
+ if (block.hasLazyApplyVShow) {
+ push(NEWLINE, `lazyApplyVShowFn.forEach(fn => fn())`)
+ }
+
if (dynamic.needsKey) {
for (const child of dynamic.children) {
const keyValue = key
diff --git a/packages/compiler-vapor/src/generators/vShow.ts b/packages/compiler-vapor/src/generators/vShow.ts
index 9a6ccefcd..701127916 100644
--- a/packages/compiler-vapor/src/generators/vShow.ts
+++ b/packages/compiler-vapor/src/generators/vShow.ts
@@ -7,12 +7,15 @@ export function genVShow(
oper: DirectiveIRNode,
context: CodegenContext,
): CodeFragment[] {
+ const { lazy, element } = oper
return [
NEWLINE,
- ...genCall(context.helper('applyVShow'), `n${oper.element}`, [
+ lazy ? `lazyApplyVShowFn.push(() => ` : undefined,
+ ...genCall(context.helper('applyVShow'), `n${element}`, [
`() => (`,
...genExpression(oper.dir.exp!, context),
`)`,
]),
+ lazy ? `)` : undefined,
]
}
diff --git a/packages/compiler-vapor/src/ir/index.ts b/packages/compiler-vapor/src/ir/index.ts
index cc0c9dcf3..7cd93b015 100644
--- a/packages/compiler-vapor/src/ir/index.ts
+++ b/packages/compiler-vapor/src/ir/index.ts
@@ -56,6 +56,7 @@ export interface BlockIRNode extends BaseIRNode {
operation: OperationNode[]
expressions: SimpleExpressionNode[]
returns: number[]
+ hasLazyApplyVShow: boolean
}
export interface RootIRNode {
@@ -187,6 +188,7 @@ export interface DirectiveIRNode extends BaseIRNode {
builtin?: boolean
asset?: boolean
modelType?: 'text' | 'dynamic' | 'radio' | 'checkbox' | 'select'
+ lazy?: boolean
}
export interface CreateComponentIRNode extends BaseIRNode {
diff --git a/packages/compiler-vapor/src/transforms/utils.ts b/packages/compiler-vapor/src/transforms/utils.ts
index b8e7adc60..99056d44c 100644
--- a/packages/compiler-vapor/src/transforms/utils.ts
+++ b/packages/compiler-vapor/src/transforms/utils.ts
@@ -31,6 +31,7 @@ export const newBlock = (node: BlockIRNode['node']): BlockIRNode => ({
returns: [],
expressions: [],
tempId: 0,
+ hasLazyApplyVShow: false,
})
export function wrapTemplate(node: ElementNode, dirs: string[]): TemplateNode {
diff --git a/packages/compiler-vapor/src/transforms/vShow.ts b/packages/compiler-vapor/src/transforms/vShow.ts
index f1135d6b0..3622cf0ff 100644
--- a/packages/compiler-vapor/src/transforms/vShow.ts
+++ b/packages/compiler-vapor/src/transforms/vShow.ts
@@ -2,11 +2,13 @@ import {
DOMErrorCodes,
ElementTypes,
ErrorCodes,
+ NodeTypes,
createCompilerError,
createDOMCompilerError,
} from '@vue/compiler-dom'
import type { DirectiveTransform } from '../transform'
import { IRNodeTypes } from '../ir'
+import { findProp, isTransitionTag } from '../utils'
export const transformVShow: DirectiveTransform = (dir, node, context) => {
const { exp, loc } = dir
@@ -27,11 +29,26 @@ export const transformVShow: DirectiveTransform = (dir, node, context) => {
return
}
+ // lazy apply vshow if the node is inside a transition with appear
+ let lazyApplyVShow = false
+ const parentNode = context.parent && context.parent.node
+ if (parentNode && parentNode.type === NodeTypes.ELEMENT) {
+ lazyApplyVShow = !!(
+ isTransitionTag(parentNode.tag) &&
+ findProp(parentNode, 'appear', false, true)
+ )
+
+ if (lazyApplyVShow) {
+ context.parent!.parent!.block.hasLazyApplyVShow = true
+ }
+ }
+
context.registerOperation({
type: IRNodeTypes.DIRECTIVE,
element: context.reference(),
dir,
name: 'show',
builtin: true,
+ lazy: lazyApplyVShow,
})
}
diff --git a/packages/runtime-vapor/src/directives/vShow.ts b/packages/runtime-vapor/src/directives/vShow.ts
index 410f0da23..5cd9c66f2 100644
--- a/packages/runtime-vapor/src/directives/vShow.ts
+++ b/packages/runtime-vapor/src/directives/vShow.ts
@@ -52,9 +52,15 @@ function setDisplay(target: Block, value: unknown): void {
el.style.display = el[vShowOriginalDisplay]!
$transition.enter(target)
} else {
- $transition.leave(target, () => {
+ // during initial render, the element is not yet inserted into the
+ // DOM, and it is hidden, no need to trigger transition
+ if (target.isConnected) {
+ $transition.leave(target, () => {
+ el.style.display = 'none'
+ })
+ } else {
el.style.display = 'none'
- })
+ }
}
} else {
el.style.display = value ? el[vShowOriginalDisplay]! : 'none'