fix(runtime-core): inherit comment nodes during block patch in production build (#10748)

close #10747
close #12650
This commit is contained in:
tonicli 2025-05-13 22:18:28 +08:00 committed by GitHub
parent 8e3435779a
commit 626450590d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 77 additions and 7 deletions

View File

@ -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(`<div>0</div>`)
expect(serializeInner(target)).toBe(`<!--comment in teleport-->`)
expect(serialize(commentNode)).toBe(`<!--comment in teleport-->`)
count.value = 1
await nextTick()
__DEV__ = true
expect(serializeInner(root)).toBe(`<div>1</div>`)
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')

View File

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