diff --git a/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts b/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts index 1de05b67b44..5e885a3d59c 100644 --- a/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts +++ b/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts @@ -13,6 +13,8 @@ import { defineComponent, openBlock, createBlock, + createElementVNode, + createElementBlock, FunctionalComponent, createCommentVNode, Fragment, @@ -673,6 +675,58 @@ describe('attribute fallthrough', () => { expect(click).toHaveBeenCalled() }) + it('should support fallthrough for nested fragments', async () => { + const toggle = ref(false) + + const Child = { + setup() { + return () => ( + openBlock(), + createElementBlock( + Fragment, + null, + [ + createCommentVNode(' comment A '), + toggle.value + ? (openBlock(), createElementBlock('span', { key: 0 }, 'Foo')) + : (openBlock(), + createElementBlock( + Fragment, + { key: 1 }, + [ + createCommentVNode(' comment B '), + createElementVNode('div', null, 'Bar') + ], + PatchFlags.STABLE_FRAGMENT | PatchFlags.DEV_ROOT_FRAGMENT + )) + ], + PatchFlags.STABLE_FRAGMENT | PatchFlags.DEV_ROOT_FRAGMENT + ) + ) + } + } + + const Root = { + setup() { + return () => (openBlock(), createBlock(Child, { class: 'red' })) + } + } + + const root = document.createElement('div') + document.body.appendChild(root) + render(h(Root), root) + + expect(root.innerHTML).toBe( + `
Bar
` + ) + + toggle.value = true + await nextTick() + expect(root.innerHTML).toBe( + `Foo` + ) + }) + // #1989 it('should not fallthrough v-model listeners with corresponding declared prop', () => { let textFoo = '' diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index d9de968a074..d39ca5d1af3 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -12,7 +12,8 @@ import { cloneVNode, VNodeArrayChildren, isVNode, - blockStack + blockStack, + Fragment } from './vnode' import { handleError, ErrorCodes } from './errorHandling' import { PatchFlags, ShapeFlags, isOn, isModelListener } from '@vue/shared' @@ -255,6 +256,11 @@ const getChildRoot = (vnode: VNode): [VNode, SetRootFn] => { if (!childRoot) { return [vnode, undefined] } + + if (childRoot.type === Fragment) { + return getChildRoot(childRoot) + } + const index = rawChildren.indexOf(childRoot) const dynamicIndex = dynamicChildren ? dynamicChildren.indexOf(childRoot) : -1 const setRoot: SetRootFn = (updatedRoot: VNode) => {