Skip to content

Commit

Permalink
fix(runtime-core): filter single root for nested DEV_ROOT_FRAGMENT (#…
Browse files Browse the repository at this point in the history
…8593)

close #5203
close #8581
close #10087
  • Loading branch information
edison1105 committed Jan 12, 2024
1 parent 8d04205 commit d35b877
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 1 deletion.
54 changes: 54 additions & 0 deletions packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
type FunctionalComponent,
createBlock,
createCommentVNode,
createElementBlock,
createElementVNode,
defineComponent,
h,
mergeProps,
Expand Down Expand Up @@ -673,6 +675,58 @@ describe('attribute fallthrough', () => {
expect(click).toHaveBeenCalled()
})

it('should support fallthrough for nested dev root 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(
`<!-- comment A --><!-- comment B --><div class="red">Bar</div>`,
)

toggle.value = true
await nextTick()
expect(root.innerHTML).toBe(
`<!-- comment A --><span class=\"red\">Foo</span>`,
)
})

// #1989
it('should not fallthrough v-model listeners with corresponding declared prop', () => {
let textFoo = ''
Expand Down
58 changes: 58 additions & 0 deletions packages/runtime-core/__tests__/scopeId.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import {
Fragment,
createBlock,
createCommentVNode,
createVNode,
defineComponent,
h,
nextTick,
nodeOps,
openBlock,
popScopeId,
pushScopeId,
ref,
render,
renderSlot,
serializeInner,
withScopeId,
} from '@vue/runtime-test'
import { withCtx } from '../src/componentRenderContext'
import { PatchFlags } from '@vue/shared'

describe('scopeId runtime support', () => {
test('should attach scopeId', () => {
Expand Down Expand Up @@ -184,6 +193,55 @@ describe('scopeId runtime support', () => {

expect(serializeInner(root)).toBe(`<div parent></div>`)
})

test('should inherit scopeId through nested DEV_ROOT_FRAGMENT with inheritAttrs: false', async () => {
const Parent = {
__scopeId: 'parent',
render() {
return h(Child, { class: 'foo' })
},
}

const ok = ref(true)
const Child = defineComponent({
inheritAttrs: false,
render() {
return (
openBlock(),
createBlock(
Fragment,
null,
[
createCommentVNode('comment1'),
ok.value
? (openBlock(), createBlock('div', { key: 0 }, 'div1'))
: (openBlock(),
createBlock(
Fragment,
{ key: 1 },
[
createCommentVNode('comment2'),
createVNode('div', null, 'div2'),
],
PatchFlags.STABLE_FRAGMENT | PatchFlags.DEV_ROOT_FRAGMENT,
)),
],
PatchFlags.STABLE_FRAGMENT | PatchFlags.DEV_ROOT_FRAGMENT,
)
)
},
})

const root = nodeOps.createElement('div')
render(h(Parent), root)
expect(serializeInner(root)).toBe(`<!--comment1--><div parent>div1</div>`)

ok.value = false
await nextTick()
expect(serializeInner(root)).toBe(
`<!--comment1--><!--comment2--><div parent>div2</div>`,
)
})
})

describe('backwards compat with <=3.0.7', () => {
Expand Down
18 changes: 17 additions & 1 deletion packages/runtime-core/src/componentRenderUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,17 @@ export function renderComponentRoot(
const getChildRoot = (vnode: VNode): [VNode, SetRootFn] => {
const rawChildren = vnode.children as VNodeArrayChildren
const dynamicChildren = vnode.dynamicChildren
const childRoot = filterSingleRoot(rawChildren)
const childRoot = filterSingleRoot(rawChildren, false)
if (!childRoot) {
return [vnode, undefined]
} else if (
__DEV__ &&
childRoot.patchFlag > 0 &&
childRoot.patchFlag & PatchFlags.DEV_ROOT_FRAGMENT
) {
return getChildRoot(childRoot)
}

const index = rawChildren.indexOf(childRoot)
const dynamicIndex = dynamicChildren ? dynamicChildren.indexOf(childRoot) : -1
const setRoot: SetRootFn = (updatedRoot: VNode) => {
Expand All @@ -287,6 +294,7 @@ const getChildRoot = (vnode: VNode): [VNode, SetRootFn] => {

export function filterSingleRoot(
children: VNodeArrayChildren,
recurse = true,
): VNode | undefined {
let singleRoot
for (let i = 0; i < children.length; i++) {
Expand All @@ -299,6 +307,14 @@ export function filterSingleRoot(
return
} else {
singleRoot = child
if (
__DEV__ &&
recurse &&
singleRoot.patchFlag > 0 &&
singleRoot.patchFlag & PatchFlags.DEV_ROOT_FRAGMENT
) {
return filterSingleRoot(singleRoot.children as VNodeArrayChildren)
}
}
}
} else {
Expand Down

0 comments on commit d35b877

Please sign in to comment.