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', () => {
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', () => {
expect(
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', () => {
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', () => {
const Comp = {
render() {

View File

@ -241,8 +241,7 @@ describe('Vapor Mode hydration', () => {
test('component with anchor insertion', async () => {
const { container, data } = await testHydration(
`
<template>
`<template>
<div>
<span/>
<components.Child/>
@ -255,13 +254,13 @@ describe('Vapor Mode hydration', () => {
},
)
expect(container.innerHTML).toMatchInlineSnapshot(
`"<div><span></span><!--[[-->foo<!--]]--><span></span></div>"`,
`"<div><span></span>foo<span></span></div>"`,
)
data.value = 'bar'
await nextTick()
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('for')

View File

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

View File

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