fix(compiler-vapor): move `next`, `child` and `nthChild` before setInsertionState (#13057)

This commit is contained in:
zhiyuanzmj 2025-06-18 09:05:43 +08:00 committed by GitHub
parent 99a8c6d34b
commit ff7fa88de1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 52 additions and 10 deletions

View File

@ -157,9 +157,9 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
const _component_Comp = _resolveComponent("Comp") const _component_Comp = _resolveComponent("Comp")
const n0 = t0() const n0 = t0()
const n3 = t1() const n3 = t1()
const n2 = _child(n3)
_setInsertionState(n3, 0) _setInsertionState(n3, 0)
const n1 = _createComponentWithFallback(_component_Comp) const n1 = _createComponentWithFallback(_component_Comp)
const n2 = _child(n3)
_renderEffect(() => { _renderEffect(() => {
_setText(n2, _toDisplayString(_ctx.bar)) _setText(n2, _toDisplayString(_ctx.bar))
_setProp(n3, "id", _ctx.foo) _setProp(n3, "id", _ctx.foo)
@ -230,6 +230,30 @@ export function render(_ctx) {
}" }"
`; `;
exports[`compile > setInsertionState > next, child and nthChild should be above the setInsertionState 1`] = `
"import { resolveComponent as _resolveComponent, child as _child, next as _next, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, nthChild as _nthChild, createIf as _createIf, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
const t0 = _template("<div></div>")
const t1 = _template("<div><div></div><!><div></div><!><div><button></button></div></div>", true)
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n6 = t1()
const n5 = _next(_child(n6))
const n7 = _nthChild(n6, 3)
const p0 = _next(n7)
const n4 = _child(p0)
_setInsertionState(n6, n5)
const n0 = _createComponentWithFallback(_component_Comp)
_setInsertionState(n6, n7)
const n1 = _createIf(() => (true), () => {
const n3 = t0()
return n3
})
_renderEffect(() => _setProp(n4, "disabled", _ctx.foo))
return n6
}"
`;
exports[`compile > static + dynamic root 1`] = ` exports[`compile > static + dynamic root 1`] = `
"import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue'; "import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue';
const t0 = _template(" ") const t0 = _template(" ")

View File

@ -220,4 +220,21 @@ describe('compile', () => {
expect(code).matchSnapshot() expect(code).matchSnapshot()
}) })
}) })
describe('setInsertionState', () => {
test('next, child and nthChild should be above the setInsertionState', () => {
const code = compile(`
<div>
<div />
<Comp />
<div />
<div v-if="true" />
<div>
<button :disabled="foo" />
</div>
</div>
`)
expect(code).toMatchSnapshot()
})
})
}) })

View File

@ -69,8 +69,8 @@ describe('compiler: children transform', () => {
</div>`, </div>`,
) )
// ensure the insertion anchor is generated before the insertion statement // ensure the insertion anchor is generated before the insertion statement
expect(code).toMatch(`const n3 = _next(_child(n4)) expect(code).toMatch(`const n3 = _next(_child(n4))`)
_setInsertionState(n4, n3)`) expect(code).toMatch(`_setInsertionState(n4, n3)`)
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
}) })
}) })

View File

@ -66,7 +66,7 @@ export function genBlockContent(
push(...genSelf(child, context)) push(...genSelf(child, context))
} }
for (const child of dynamic.children) { for (const child of dynamic.children) {
push(...genChildren(child, context, `n${child.id!}`)) push(...genChildren(child, context, push, `n${child.id!}`))
} }
push(...genOperations(operation, context)) push(...genOperations(operation, context))

View File

@ -41,6 +41,7 @@ export function genSelf(
export function genChildren( export function genChildren(
dynamic: IRDynamicInfo, dynamic: IRDynamicInfo,
context: CodegenContext, context: CodegenContext,
pushBlock: (...items: CodeFragment[]) => number,
from: string = `n${dynamic.id}`, from: string = `n${dynamic.id}`,
): CodeFragment[] { ): CodeFragment[] {
const { helper } = context const { helper } = context
@ -72,17 +73,17 @@ export function genChildren(
// p for "placeholder" variables that are meant for possible reuse by // p for "placeholder" variables that are meant for possible reuse by
// other access paths // other access paths
const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}` const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}`
push(NEWLINE, `const ${variable} = `) pushBlock(NEWLINE, `const ${variable} = `)
if (prev) { if (prev) {
if (elementIndex - prev[1] === 1) { if (elementIndex - prev[1] === 1) {
push(...genCall(helper('next'), prev[0])) pushBlock(...genCall(helper('next'), prev[0]))
} else { } else {
push(...genCall(helper('nthChild'), from, String(elementIndex))) pushBlock(...genCall(helper('nthChild'), from, String(elementIndex)))
} }
} else { } else {
if (elementIndex === 0) { if (elementIndex === 0) {
push(...genCall(helper('child'), from)) pushBlock(...genCall(helper('child'), from))
} else { } else {
// check if there's a node that we can reuse from // check if there's a node that we can reuse from
let init = genCall(helper('child'), from) let init = genCall(helper('child'), from)
@ -91,7 +92,7 @@ export function genChildren(
} else if (elementIndex > 1) { } else if (elementIndex > 1) {
init = genCall(helper('nthChild'), from, String(elementIndex)) init = genCall(helper('nthChild'), from, String(elementIndex))
} }
push(...init) pushBlock(...init)
} }
} }
@ -109,7 +110,7 @@ export function genChildren(
if (childrenToGen.length) { if (childrenToGen.length) {
for (const [child, from] of childrenToGen) { for (const [child, from] of childrenToGen) {
push(...genChildren(child, context, from)) push(...genChildren(child, context, pushBlock, from))
} }
} }