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)
}
}