createTextVNode,
createVNode,
withDirectives,
- vModelCheckbox
+ vModelCheckbox,
+ renderSlot
} from '@vue/runtime-dom'
import { renderToString, SSRContext } from '@vue/server-renderer'
import { PatchFlags } from '../../shared/src'
expect((container.firstChild!.firstChild as any)._value).toBe(true)
})
+ // #5728
+ test('empty text node in slot', () => {
+ const Comp = {
+ render(this: any) {
+ return renderSlot(this.$slots, 'default', {}, () => [
+ createTextVNode('')
+ ])
+ }
+ }
+ const { container, vnode } = mountWithHydration('<!--[--><!--]-->', () => h(Comp))
+ expect(container.childNodes.length).toBe(3)
+ const text = container.childNodes[1]
+ expect(text.nodeType).toBe(3)
+ expect(vnode.el).toBe(container.childNodes[0])
+ // component => slot fragment => text node
+ expect((vnode as any).component?.subTree.children[0].el).toBe(text)
+ })
+
describe('mismatch handling', () => {
test('text node', () => {
const { container } = mountWithHydration(`foo`, () => 'bar')
switch (type) {
case Text:
if (domType !== DOMNodeTypes.TEXT) {
- nextNode = onMismatch()
+ // #5728 empty text node inside a slot can cause hydration failure
+ // because the server rendered HTML won't contain a text node
+ if (vnode.children === '') {
+ insert(
+ (vnode.el = document.createTextNode('')),
+ node.parentElement!,
+ node
+ )
+ nextNode = node
+ } else {
+ nextNode = onMismatch()
+ }
} else {
if ((node as Text).data !== vnode.children) {
hasMismatch = true