diff --git a/packages/runtime-core/__tests__/components/Teleport.spec.ts b/packages/runtime-core/__tests__/components/Teleport.spec.ts index 1229c5325..4c35b1f2d 100644 --- a/packages/runtime-core/__tests__/components/Teleport.spec.ts +++ b/packages/runtime-core/__tests__/components/Teleport.spec.ts @@ -14,10 +14,19 @@ import { h as originalH, ref, render, + serialize, serializeInner, withDirectives, } from '@vue/runtime-test' -import { Fragment, createCommentVNode, createVNode } from '../../src/vnode' +import { + Fragment, + createBlock, + createCommentVNode, + createTextVNode, + createVNode, + openBlock, +} from '../../src/vnode' +import { toDisplayString } from '@vue/shared' import { compile, createApp as createDOMApp, render as domRender } from 'vue' import type { HMRRuntime } from '../../src/hmr' @@ -248,6 +257,39 @@ describe('renderer: teleport', () => { expect(serializeInner(target)).toBe(`teleported`) }) + test('should traverse comment node after updating in optimize mode', async () => { + const target = nodeOps.createElement('div') + const root = nodeOps.createElement('div') + const count = ref(0) + let teleport + + __DEV__ = false + render( + h(() => { + teleport = + (openBlock(), + createBlock(Teleport, { to: target }, [ + createCommentVNode('comment in teleport'), + ])) + return h('div', null, [ + createTextVNode(toDisplayString(count.value)), + teleport, + ]) + }), + root, + ) + const commentNode = teleport!.children[0].el + expect(serializeInner(root)).toBe(`
0
`) + expect(serializeInner(target)).toBe(``) + expect(serialize(commentNode)).toBe(``) + + count.value = 1 + await nextTick() + __DEV__ = true + expect(serializeInner(root)).toBe(`
1
`) + expect(teleport!.children[0].el).toBe(commentNode) + }) + test('should remove children when unmounted', () => { const target = nodeOps.createElement('div') const root = nodeOps.createElement('div') @@ -274,6 +316,34 @@ describe('renderer: teleport', () => { testUnmount({ to: null, disabled: true }) }) + // #10747 + test('should unmount correctly when using top level comment in teleport', async () => { + const target = nodeOps.createElement('div') + const root = nodeOps.createElement('div') + const count = ref(0) + + __DEV__ = false + render( + h(() => { + return h('div', null, [ + createTextVNode(toDisplayString(count.value)), + (openBlock(), + createBlock(Teleport, { to: target }, [ + createCommentVNode('comment in teleport'), + ])), + ]) + }), + root, + ) + + count.value = 1 + + await nextTick() + __DEV__ = true + render(null, root) + expect(root.children.length).toBe(0) + }) + test('component with multi roots should be removed when unmounted', () => { const target = nodeOps.createElement('div') const root = nodeOps.createElement('div') diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index e43348f85..334858b7b 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2503,13 +2503,13 @@ export function traverseStaticChildren( if (c2.type === Text) { c2.el = c1.el } - if (__DEV__) { - // #2324 also inherit for comment nodes, but not placeholders (e.g. v-if which - // would have received .el during block patch) - if (c2.type === Comment && !c2.el) { - c2.el = c1.el - } + // #2324 also inherit for comment nodes, but not placeholders (e.g. v-if which + // would have received .el during block patch) + if (c2.type === Comment && !c2.el) { + c2.el = c1.el + } + if (__DEV__) { c2.el && (c2.el.__vnode = c2) } }