wip: save

This commit is contained in:
daiwei 2025-04-22 17:40:31 +08:00
parent e5dd701291
commit 9c30fd4db9
6 changed files with 107 additions and 69 deletions

View File

@ -398,24 +398,6 @@ describe('ssr: element', () => {
}) })
describe('dynamic child anchor', () => { describe('dynamic child anchor', () => {
test('component with element siblings', () => {
expect(
getCompiledString(`
<div>
<div/>
<Comp1/>
<div/>
</div>
`),
).toMatchInlineSnapshot(`
"\`<div><div></div>\`)
_push("<!--[[-->")
_push(_ssrRenderComponent(_component_Comp1, null, null, _parent))
_push("<!--]]-->")
_push(\`<div></div></div>\`"
`)
})
test('with consecutive components', () => { test('with consecutive components', () => {
expect( expect(
getCompiledString(` getCompiledString(`

View File

@ -428,5 +428,24 @@ function shouldAddDynamicAnchor(
} }
} }
return hasStaticPreviousSibling && hasStaticNextSibling let hasConsecutiveDynamicNodes = false
if (index > 0 && index < len - 1) {
if (index > 0 && !isStaticElement(children[index - 1])) {
hasConsecutiveDynamicNodes = true
}
if (
!hasConsecutiveDynamicNodes &&
index < len - 1 &&
!isStaticElement(children[index + 1])
) {
hasConsecutiveDynamicNodes = true
}
}
return (
hasStaticPreviousSibling &&
hasStaticNextSibling &&
hasConsecutiveDynamicNodes
)
} }

View File

@ -1844,20 +1844,6 @@ describe('SSR hydration', () => {
}) })
describe('dynamic child anchor', () => { describe('dynamic child anchor', () => {
test('component with element siblings', () => {
const Comp = {
render() {
return createTextVNode('foo')
},
}
const { vnode, container } = mountWithHydration(
`<div><span></span><!--[[-->foo<!--]]--><span></span></div>`,
() => h('div', null, [h('span'), h(Comp), h('span')]),
)
expect(vnode.el).toBe(container.firstChild)
expect(`Hydration children mismatch`).not.toHaveBeenWarned()
})
test('with consecutive components', () => { test('with consecutive components', () => {
const Comp = { const Comp = {
render() { render() {

View File

@ -241,8 +241,7 @@ describe('Vapor Mode hydration', () => {
test('component with anchor insertion', async () => { test('component with anchor insertion', async () => {
const { container, data } = await testHydration( const { container, data } = await testHydration(
` `<template>
<template>
<div> <div>
<span/> <span/>
<components.Child/> <components.Child/>
@ -255,13 +254,13 @@ describe('Vapor Mode hydration', () => {
}, },
) )
expect(container.innerHTML).toMatchInlineSnapshot( expect(container.innerHTML).toMatchInlineSnapshot(
`"<div><span></span><!--[[-->foo<!--]]--><span></span></div>"`, `"<div><span></span>foo<span></span></div>"`,
) )
data.value = 'bar' data.value = 'bar'
await nextTick() await nextTick()
expect(container.innerHTML).toMatchInlineSnapshot( expect(container.innerHTML).toMatchInlineSnapshot(
`"<div><span></span><!--[[-->bar<!--]]--><span></span></div>"`, `"<div><span></span>bar<span></span></div>"`,
) )
}) })
@ -291,6 +290,56 @@ describe('Vapor Mode hydration', () => {
) )
}) })
test('mixed component and element with anchor insertion', async () => {
const { container, data } = await testHydration(
`<template>
<div>
<span/>
<components.Child/>
<span/>
<components.Child/>
<span/>
</div>
</template>
`,
{
Child: `<template>{{ data }}</template>`,
},
)
expect(container.innerHTML).toMatchInlineSnapshot(
`"<div><span></span>foo<span></span>foo<span></span></div>"`,
)
data.value = 'bar'
await nextTick()
expect(container.innerHTML).toMatchInlineSnapshot(
`"<div><span></span>bar<span></span>bar<span></span></div>"`,
)
})
test.todo('mixed component and text with anchor insertion', async () => {
const { container, data } = await testHydration(
`<template>
<div>
<span/>
<components.Child/>
{{ data }}
<components.Child/>
<span/>
</div>
</template>
`,
{
Child: `<template>{{ data }}</template>`,
},
)
expect(container.innerHTML).toMatchInlineSnapshot(``)
data.value = 'bar'
await nextTick()
expect(container.innerHTML).toMatchInlineSnapshot(``)
})
test.todo('if') test.todo('if')
test.todo('for') test.todo('for')

View File

@ -75,45 +75,47 @@ function locateHydrationNodeImpl() {
// prepend / firstChild // prepend / firstChild
if (insertionAnchor === 0) { if (insertionAnchor === 0) {
node = child(insertionParent!) node = child(insertionParent!)
} else { } else if (insertionParent && insertionAnchor) {
// dynamic child anchor `<!--[[-->` // dynamic child anchor `<!--[[-->`
if (insertionAnchor && isDynamicStart(insertionAnchor)) { if (insertionAnchor && isDynamicStart(insertionAnchor)) {
const anchor = (insertionParent!.lds = insertionParent!.lds const anchor = (insertionParent!.$lds = insertionParent!.$lds
? // continuous dynamic children, the next dynamic start must exist ? // continuous dynamic children, the next dynamic start must exist
locateNextDynamicStart(insertionParent!.lds)! locateNextDynamicStart(insertionParent!.$lds)!
: insertionAnchor) : insertionAnchor)
node = anchor.nextSibling node = anchor.nextSibling
} else { } else {
node = insertionAnchor node = insertionAnchor
? insertionAnchor.previousSibling }
: insertionParent } else {
? insertionParent.lastChild node = insertionAnchor
: currentHydrationNode ? insertionAnchor.previousSibling
if (node && isComment(node, ']')) { : insertionParent
// fragment backward search ? insertionParent.lastChild
if (node.$fs) { : currentHydrationNode
// already cached matching fragment start if (node && isComment(node, ']')) {
node = node.$fs // fragment backward search
} else { if (node.$fs) {
let cur: Node | null = node // already cached matching fragment start
let curFragEnd = node node = node.$fs
let fragDepth = 0 } else {
node = null let cur: Node | null = node
while (cur) { let curFragEnd = node
cur = cur.previousSibling let fragDepth = 0
if (cur) { node = null
if (isComment(cur, '[')) { while (cur) {
curFragEnd.$fs = cur cur = cur.previousSibling
if (!fragDepth) { if (cur) {
node = cur if (isComment(cur, '[')) {
break curFragEnd.$fs = cur
} else { if (!fragDepth) {
fragDepth-- node = cur
} break
} else if (isComment(cur, ']')) { } else {
curFragEnd = cur fragDepth--
fragDepth++
} }
} else if (isComment(cur, ']')) {
curFragEnd = cur
fragDepth++
} }
} }
} }

View File

@ -1,7 +1,7 @@
export let insertionParent: export let insertionParent:
| (ParentNode & { | (ParentNode & {
// cached the last dynamic start anchor // cached the last dynamic start anchor
lds?: Anchor $lds?: Anchor
}) })
| undefined | undefined
export let insertionAnchor: Node | 0 | undefined | null export let insertionAnchor: Node | 0 | undefined | null