wip: hydration for slots
This commit is contained in:
parent
ca34d4aa29
commit
42a421a93f
|
@ -44,7 +44,7 @@ export function genOperationWithInsertionState(
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
const [frag, push] = buildCodeFragment()
|
const [frag, push] = buildCodeFragment()
|
||||||
if (isBlockOperation(oper) && oper.parent) {
|
if (isBlockOperation(oper) && oper.parent) {
|
||||||
push(...genInsertionstate(oper, context))
|
push(...genInsertionState(oper, context))
|
||||||
}
|
}
|
||||||
push(...genOperation(oper, context))
|
push(...genOperation(oper, context))
|
||||||
return frag
|
return frag
|
||||||
|
@ -152,7 +152,7 @@ export function genEffect(
|
||||||
return frag
|
return frag
|
||||||
}
|
}
|
||||||
|
|
||||||
function genInsertionstate(
|
function genInsertionState(
|
||||||
operation: InsertionStateTypes,
|
operation: InsertionStateTypes,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
|
|
|
@ -1531,6 +1531,20 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data.value.splice(0, 1)
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<span></span>` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<span>b</span>` +
|
||||||
|
`<span>c</span>` +
|
||||||
|
`<span>d</span>` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`<span></span>` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('consecutive v-for with anchor insertion', async () => {
|
test('consecutive v-for with anchor insertion', async () => {
|
||||||
|
@ -1583,20 +1597,377 @@ describe('Vapor Mode hydration', () => {
|
||||||
`<span></span>` +
|
`<span></span>` +
|
||||||
`</div>`,
|
`</div>`,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data.value.splice(0, 2)
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<span></span>` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<span>c</span>` +
|
||||||
|
`<span>d</span>` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<span>c</span>` +
|
||||||
|
`<span>d</span>` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`<span></span>` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO wait for slots hydration support
|
test('v-for on component', async () => {
|
||||||
test.todo('v-for on component', async () => {})
|
const { container, data } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<div>
|
||||||
|
<components.Child v-for="item in data" :key="item"/>
|
||||||
|
</div>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template><div>comp</div></template>`,
|
||||||
|
},
|
||||||
|
ref(['a', 'b', 'c']),
|
||||||
|
)
|
||||||
|
|
||||||
// TODO wait for slots hydration support
|
expect(container.innerHTML).toBe(
|
||||||
test.todo('on fragment component', async () => {})
|
`<div>` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<div>comp</div>` +
|
||||||
|
`<div>comp</div>` +
|
||||||
|
`<div>comp</div>` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.value.push('d')
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<div>comp</div>` +
|
||||||
|
`<div>comp</div>` +
|
||||||
|
`<div>comp</div>` +
|
||||||
|
`<div>comp</div>` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-for on component with slots', async () => {
|
||||||
|
const { container, data } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<div>
|
||||||
|
<components.Child v-for="item in data" :key="item">
|
||||||
|
<span>{{ item }}</span>
|
||||||
|
</components.Child>
|
||||||
|
</div>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template><slot/></template>`,
|
||||||
|
},
|
||||||
|
ref(['a', 'b', 'c']),
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<!--[--><span>a</span><!--]--><!--slot-->` +
|
||||||
|
`<!--[--><span>b</span><!--]--><!--slot-->` +
|
||||||
|
`<!--[--><span>c</span><!--]--><!--slot-->` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.value.push('d')
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<!--[--><span>a</span><!--]--><!--slot-->` +
|
||||||
|
`<!--[--><span>b</span><!--]--><!--slot-->` +
|
||||||
|
`<!--[--><span>c</span><!--]--><!--slot-->` +
|
||||||
|
`<span>d</span><!--slot-->` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('on fragment component', async () => {
|
||||||
|
const { container, data } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<div>
|
||||||
|
<components.Child v-for="item in data" :key="item"/>
|
||||||
|
</div>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template><div>foo</div>-bar-</template>`,
|
||||||
|
},
|
||||||
|
ref(['a', 'b', 'c']),
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
||||||
|
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
||||||
|
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.value.push('d')
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<div>` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
||||||
|
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
||||||
|
`<!--[--><div>foo</div>-bar-<!--]-->` +
|
||||||
|
`<div>foo</div>-bar-` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`</div>`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
// TODO wait for vapor TransitionGroup support
|
// TODO wait for vapor TransitionGroup support
|
||||||
// v-for inside TransitionGroup does not render as a fragment
|
// v-for inside TransitionGroup does not render as a fragment
|
||||||
test.todo('v-for in TransitionGroup', async () => {})
|
test.todo('v-for in TransitionGroup', async () => {})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.todo('slots')
|
describe('slots', () => {
|
||||||
|
test('basic slot', async () => {
|
||||||
|
const { data, container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<span>{{data}}</span>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template><slot/></template>`,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[--><span>foo</span><!--]--><!--slot-->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.value = 'bar'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[--><span>bar</span><!--]--><!--slot-->`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('named slot', async () => {
|
||||||
|
const { data, container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<template #foo>
|
||||||
|
<span>{{data}}</span>
|
||||||
|
</template>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template><slot name="foo"/></template>`,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[--><span>foo</span><!--]--><!--slot-->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.value = 'bar'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[--><span>bar</span><!--]--><!--slot-->`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('named slot with v-if', async () => {
|
||||||
|
const { data, container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<template #foo v-if="data">
|
||||||
|
<span>{{data}}</span>
|
||||||
|
</template>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template><slot name="foo"/></template>`,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[--><span>foo</span><!--]--><!--slot-->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.value = false
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(`<!--[--><!--]--><!--slot-->`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('named slot with v-if and v-for', async () => {
|
||||||
|
const data = reactive({
|
||||||
|
show: true,
|
||||||
|
items: ['a', 'b', 'c'],
|
||||||
|
})
|
||||||
|
const { container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<template #foo v-if="data.show">
|
||||||
|
<span v-for="item in data.items" :key="item">{{item}}</span>
|
||||||
|
</template>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template><slot name="foo"/></template>`,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[-->` +
|
||||||
|
`<!--[--><span>a</span><span>b</span><span>c</span><!--]-->` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`<!--slot-->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.show = false
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[--><!--[--><!--]--><!--]--><!--slot-->`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('with anchor insertion', async () => {
|
||||||
|
const { data, container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<span/>
|
||||||
|
<span>{{data}}</span>
|
||||||
|
<span/>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template><slot/></template>`,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[-->` +
|
||||||
|
`<span></span>` +
|
||||||
|
`<span>foo</span>` +
|
||||||
|
`<span></span>` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`<!--slot-->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.value = 'bar'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[-->` +
|
||||||
|
`<span></span>` +
|
||||||
|
`<span>bar</span>` +
|
||||||
|
`<span></span>` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`<!--slot-->`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('with multi level anchor insertion', async () => {
|
||||||
|
const { data, container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<span/>
|
||||||
|
<span>{{data}}</span>
|
||||||
|
<span/>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `
|
||||||
|
<template>
|
||||||
|
<div/>
|
||||||
|
<div/>
|
||||||
|
<slot/>
|
||||||
|
<div/>
|
||||||
|
</div>
|
||||||
|
</template>`,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[-->` +
|
||||||
|
`<div></div>` +
|
||||||
|
`<div></div>` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<span></span>` +
|
||||||
|
`<span>foo</span>` +
|
||||||
|
`<span></span>` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`<!--slot-->` +
|
||||||
|
`<div></div>` +
|
||||||
|
`<!--]-->`,
|
||||||
|
)
|
||||||
|
|
||||||
|
data.value = 'bar'
|
||||||
|
await nextTick()
|
||||||
|
expect(container.innerHTML).toBe(
|
||||||
|
`<!--[-->` +
|
||||||
|
`<div></div>` +
|
||||||
|
`<div></div>` +
|
||||||
|
`<!--[-->` +
|
||||||
|
`<span></span>` +
|
||||||
|
`<span>bar</span>` +
|
||||||
|
`<span></span>` +
|
||||||
|
`<!--]-->` +
|
||||||
|
`<!--slot-->` +
|
||||||
|
`<div></div>` +
|
||||||
|
`<!--]-->`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// problem is next child is incorrect after slot
|
||||||
|
test.todo('mixed slot and text node', async () => {
|
||||||
|
const data = reactive({
|
||||||
|
text: 'foo',
|
||||||
|
msg: 'hi',
|
||||||
|
})
|
||||||
|
const { container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<span>{{data.text}}</span>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template><div><slot/>{{data.msg}}</div></template>`,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
|
`"<div><!--[--><span>foo</span><!--]--><!--slot-->hi</div>"`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.todo('mixed slot and element', async () => {
|
||||||
|
const data = reactive({
|
||||||
|
text: 'foo',
|
||||||
|
msg: 'hi',
|
||||||
|
})
|
||||||
|
const { container } = await testHydration(
|
||||||
|
`<template>
|
||||||
|
<components.Child>
|
||||||
|
<span>{{data.text}}</span>
|
||||||
|
</components.Child>
|
||||||
|
</template>`,
|
||||||
|
{
|
||||||
|
Child: `<template><div><slot/><div>{{data.msg}}</div></div></template>`,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
|
`"<div><!--hi--><span>foo</span><!--]--><!--slot--><div>hi</div></div>"`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// mixed slot and component
|
||||||
|
// mixed slot and fragment component
|
||||||
|
// mixed slot and v-if
|
||||||
|
// mixed slot and v-for
|
||||||
|
})
|
||||||
|
|
||||||
// test('element with ref', () => {
|
// test('element with ref', () => {
|
||||||
// const el = ref()
|
// const el = ref()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { isArray, isVaporFragmentEndAnchor } from '@vue/shared'
|
import { isArray } from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
type VaporComponentInstance,
|
type VaporComponentInstance,
|
||||||
isVaporComponent,
|
isVaporComponent,
|
||||||
|
@ -100,10 +100,10 @@ export class DynamicFragment extends VaporFragment {
|
||||||
} else {
|
} else {
|
||||||
// find next sibling dynamic fragment end anchor
|
// find next sibling dynamic fragment end anchor
|
||||||
const anchor = nextVaporFragmentAnchor(currentHydrationNode!, label)!
|
const anchor = nextVaporFragmentAnchor(currentHydrationNode!, label)!
|
||||||
if (anchor && isVaporFragmentEndAnchor(anchor)) {
|
if (anchor) {
|
||||||
this.anchor = anchor
|
this.anchor = anchor
|
||||||
} else if (__DEV__) {
|
} else if (__DEV__) {
|
||||||
// TODO warning
|
// TODO warning, should not happen
|
||||||
warn(`DynamicFragment anchor not found...`)
|
warn(`DynamicFragment anchor not found...`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,22 @@
|
||||||
import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
|
import {
|
||||||
|
EMPTY_OBJ,
|
||||||
|
NO,
|
||||||
|
SLOT_ANCHOR_LABEL,
|
||||||
|
hasOwn,
|
||||||
|
isArray,
|
||||||
|
isFunction,
|
||||||
|
} from '@vue/shared'
|
||||||
import { type Block, type BlockFn, DynamicFragment, insert } from './block'
|
import { type Block, type BlockFn, DynamicFragment, insert } from './block'
|
||||||
import { rawPropsProxyHandlers } from './componentProps'
|
import { rawPropsProxyHandlers } from './componentProps'
|
||||||
import { currentInstance, isRef } from '@vue/runtime-dom'
|
import { currentInstance, isRef } from '@vue/runtime-dom'
|
||||||
import type { LooseRawProps, VaporComponentInstance } from './component'
|
import type { LooseRawProps, VaporComponentInstance } from './component'
|
||||||
import { renderEffect } from './renderEffect'
|
import { renderEffect } from './renderEffect'
|
||||||
import { insertionAnchor, insertionParent } from './insertionState'
|
import {
|
||||||
import { isHydrating, locateHydrationNode } from './dom/hydration'
|
insertionAnchor,
|
||||||
|
insertionParent,
|
||||||
|
resetInsertionState,
|
||||||
|
} from './insertionState'
|
||||||
|
import { isHydrating } from './dom/hydration'
|
||||||
|
|
||||||
export type RawSlots = Record<string, VaporSlot> & {
|
export type RawSlots = Record<string, VaporSlot> & {
|
||||||
$?: DynamicSlotSource[]
|
$?: DynamicSlotSource[]
|
||||||
|
@ -94,9 +105,7 @@ export function createSlot(
|
||||||
): Block {
|
): Block {
|
||||||
const _insertionParent = insertionParent
|
const _insertionParent = insertionParent
|
||||||
const _insertionAnchor = insertionAnchor
|
const _insertionAnchor = insertionAnchor
|
||||||
if (isHydrating) {
|
if (!isHydrating) resetInsertionState()
|
||||||
locateHydrationNode()
|
|
||||||
}
|
|
||||||
|
|
||||||
const instance = currentInstance as VaporComponentInstance
|
const instance = currentInstance as VaporComponentInstance
|
||||||
const rawSlots = instance.rawSlots
|
const rawSlots = instance.rawSlots
|
||||||
|
@ -115,7 +124,7 @@ export function createSlot(
|
||||||
fallback,
|
fallback,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
fragment = __DEV__ ? new DynamicFragment('slot') : new DynamicFragment()
|
fragment = new DynamicFragment(SLOT_ANCHOR_LABEL)
|
||||||
const isDynamicName = isFunction(name)
|
const isDynamicName = isFunction(name)
|
||||||
const renderSlot = () => {
|
const renderSlot = () => {
|
||||||
const slot = getSlot(rawSlots, isFunction(name) ? name() : name)
|
const slot = getSlot(rawSlots, isFunction(name) ? name() : name)
|
||||||
|
|
|
@ -42,7 +42,7 @@ export function withHydration(container: ParentNode, fn: () => void): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
export let adoptTemplate: (node: Node, template: string) => Node | null
|
export let adoptTemplate: (node: Node, template: string) => Node | null
|
||||||
export let locateHydrationNode: (isFragment?: boolean) => void
|
export let locateHydrationNode: (hasFragmentAnchor?: boolean) => void
|
||||||
|
|
||||||
type Anchor = Comment & {
|
type Anchor = Comment & {
|
||||||
// cached matching fragment start to avoid repeated traversal
|
// cached matching fragment start to avoid repeated traversal
|
||||||
|
@ -94,10 +94,16 @@ function locateHydrationNodeImpl(hasFragmentAnchor?: boolean) {
|
||||||
} else {
|
} else {
|
||||||
node = insertionParent ? insertionParent.lastChild : currentHydrationNode
|
node = insertionParent ? insertionParent.lastChild : currentHydrationNode
|
||||||
|
|
||||||
|
// if current node is fragment start anchor, find the next one
|
||||||
|
if (node && isComment(node, '[')) {
|
||||||
|
node = node.nextSibling
|
||||||
|
}
|
||||||
// if the last child is a vapor fragment end anchor, find the previous one
|
// if the last child is a vapor fragment end anchor, find the previous one
|
||||||
if (hasFragmentAnchor && node && isVaporFragmentEndAnchor(node)) {
|
else if (hasFragmentAnchor && node && isVaporFragmentEndAnchor(node)) {
|
||||||
let previous = node.previousSibling
|
node = node.previousSibling
|
||||||
if (previous) node = previous
|
if (__DEV__ && !node) {
|
||||||
|
// TODO warning, should not happen
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node && isComment(node, ']')) {
|
if (node && isComment(node, ']')) {
|
||||||
|
|
|
@ -446,7 +446,7 @@ function testRender(type: string, render: typeof renderToString) {
|
||||||
).toBe(
|
).toBe(
|
||||||
`<div>parent<div class="child">` +
|
`<div>parent<div class="child">` +
|
||||||
`<!--[--><span>from slot</span><!--]-->` +
|
`<!--[--><span>from slot</span><!--]-->` +
|
||||||
`</div></div>`,
|
`<!--slot--></div></div>`,
|
||||||
)
|
)
|
||||||
|
|
||||||
// test fallback
|
// test fallback
|
||||||
|
@ -461,7 +461,7 @@ function testRender(type: string, render: typeof renderToString) {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(
|
).toBe(
|
||||||
`<div>parent<div class="child"><!--[-->fallback<!--]--></div></div>`,
|
`<div>parent<div class="child"><!--[-->fallback<!--]--><!--slot--></div></div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -507,7 +507,7 @@ function testRender(type: string, render: typeof renderToString) {
|
||||||
).toBe(
|
).toBe(
|
||||||
`<div>parent<div class="child">` +
|
`<div>parent<div class="child">` +
|
||||||
`<!--[--><span>from slot</span><!--]-->` +
|
`<!--[--><span>from slot</span><!--]-->` +
|
||||||
`</div></div>`,
|
`<!--slot--></div></div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -525,7 +525,7 @@ function testRender(type: string, render: typeof renderToString) {
|
||||||
expect(await render(app)).toBe(
|
expect(await render(app)).toBe(
|
||||||
`<div>parent<div class="child">` +
|
`<div>parent<div class="child">` +
|
||||||
`<!--[--><span>from slot</span><!--]-->` +
|
`<!--[--><span>from slot</span><!--]-->` +
|
||||||
`</div></div>`,
|
`<!--slot--></div></div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -572,7 +572,7 @@ function testRender(type: string, render: typeof renderToString) {
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(await render(app)).toBe(
|
expect(await render(app)).toBe(
|
||||||
`<div><!--[--><!--[-->hello<!--]--><!--]--></div>`,
|
`<div><!--[--><!--[-->hello<!--]--><!--slot--><!--]--><!--slot--></div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -593,7 +593,7 @@ function testRender(type: string, render: typeof renderToString) {
|
||||||
|
|
||||||
expect(await render(app)).toBe(
|
expect(await render(app)).toBe(
|
||||||
// should only have a single fragment
|
// should only have a single fragment
|
||||||
`<div><!--[--><!--]--></div>`,
|
`<div><!--[--><!--]--><!--slot--></div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -614,7 +614,7 @@ function testRender(type: string, render: typeof renderToString) {
|
||||||
|
|
||||||
expect(await render(app)).toBe(
|
expect(await render(app)).toBe(
|
||||||
// should only have a single fragment
|
// should only have a single fragment
|
||||||
`<div><!--[-->fallback<!--]--></div>`,
|
`<div><!--[-->fallback<!--]--><!--slot--></div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -15,7 +15,7 @@ describe('ssr: dynamic component', () => {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(
|
).toBe(
|
||||||
`<div><!--[--><span>slot</span><!--]--></div><!--dynamic-component-->`,
|
`<div><!--[--><span>slot</span><!--]--><!--slot--></div><!--dynamic-component-->`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ describe('ssr: dynamic component', () => {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(
|
).toBe(
|
||||||
`<div>test<!--[--><span>slot</span><!--]--></div><!--dynamic-component-->`,
|
`<div>test<!--[--><span>slot</span><!--]--><!--slot--></div><!--dynamic-component-->`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,9 @@ describe('ssr: scopedId runtime behavior', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await renderToString(createApp(Comp))
|
const result = await renderToString(createApp(Comp))
|
||||||
expect(result).toBe(`<!--[--><div parent wrapper-s child></div><!--]-->`)
|
expect(result).toBe(
|
||||||
|
`<!--[--><div parent wrapper-s child></div><!--]--><!--slot-->`,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// #2892
|
// #2892
|
||||||
|
@ -150,8 +152,8 @@ describe('ssr: scopedId runtime behavior', () => {
|
||||||
const result = await renderToString(createApp(Root))
|
const result = await renderToString(createApp(Root))
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
`<div class="wrapper" root slotted wrapper>` +
|
`<div class="wrapper" root slotted wrapper>` +
|
||||||
`<!--[--><!--[--><div root slotted-s wrapper-s></div><!--]--><!--]-->` +
|
`<!--[--><!--[--><div root slotted-s wrapper-s></div><!--]--><!--slot--><!--]-->` +
|
||||||
`</div>`,
|
`<!--slot--></div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -265,8 +267,8 @@ describe('ssr: scopedId runtime behavior', () => {
|
||||||
const result = await renderToString(createApp(Root))
|
const result = await renderToString(createApp(Root))
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
`<div class="wrapper" root slotted wrapper>` +
|
`<div class="wrapper" root slotted wrapper>` +
|
||||||
`<!--[--><!--[--><div root slotted-s wrapper-s></div><!--]--><!--]-->` +
|
`<!--[--><!--[--><div root slotted-s wrapper-s></div><!--]--><!--slot--><!--]-->` +
|
||||||
`</div>`,
|
`<!--slot--></div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -16,7 +16,7 @@ describe('ssr: slot', () => {
|
||||||
template: `<one>hello</one>`,
|
template: `<one>hello</one>`,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(`<div><!--[-->hello<!--]--></div>`)
|
).toBe(`<div><!--[-->hello<!--]--><!--slot--></div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('element slot', async () => {
|
test('element slot', async () => {
|
||||||
|
@ -27,7 +27,7 @@ describe('ssr: slot', () => {
|
||||||
template: `<one><div>hi</div></one>`,
|
template: `<one><div>hi</div></one>`,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(`<div><!--[--><div>hi</div><!--]--></div>`)
|
).toBe(`<div><!--[--><div>hi</div><!--]--><!--slot--></div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('empty slot', async () => {
|
test('empty slot', async () => {
|
||||||
|
@ -42,7 +42,7 @@ describe('ssr: slot', () => {
|
||||||
template: `<one><template v-if="false"/></one>`,
|
template: `<one><template v-if="false"/></one>`,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(`<div><!--[--><!--]--></div>`)
|
).toBe(`<div><!--[--><!--]--><!--slot--></div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('empty slot (manual comments)', async () => {
|
test('empty slot (manual comments)', async () => {
|
||||||
|
@ -57,7 +57,7 @@ describe('ssr: slot', () => {
|
||||||
template: `<one><!--hello--></one>`,
|
template: `<one><!--hello--></one>`,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(`<div><!--[--><!--]--></div>`)
|
).toBe(`<div><!--[--><!--]--><!--slot--></div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('empty slot (multi-line comments)', async () => {
|
test('empty slot (multi-line comments)', async () => {
|
||||||
|
@ -72,7 +72,7 @@ describe('ssr: slot', () => {
|
||||||
template: `<one><!--he\nllo--></one>`,
|
template: `<one><!--he\nllo--></one>`,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(`<div><!--[--><!--]--></div>`)
|
).toBe(`<div><!--[--><!--]--><!--slot--></div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('multiple elements', async () => {
|
test('multiple elements', async () => {
|
||||||
|
@ -83,7 +83,7 @@ describe('ssr: slot', () => {
|
||||||
template: `<one><div>one</div><div>two</div></one>`,
|
template: `<one><div>one</div><div>two</div></one>`,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(`<div><!--[--><div>one</div><div>two</div><!--]--></div>`)
|
).toBe(`<div><!--[--><div>one</div><div>two</div><!--]--><!--slot--></div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('fragment slot (template v-if)', async () => {
|
test('fragment slot (template v-if)', async () => {
|
||||||
|
@ -94,7 +94,9 @@ describe('ssr: slot', () => {
|
||||||
template: `<one><template v-if="true">hello</template></one>`,
|
template: `<one><template v-if="true">hello</template></one>`,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(`<div><!--[--><!--[-->hello<!--]--><!--if--><!--]--></div>`)
|
).toBe(
|
||||||
|
`<div><!--[--><!--[-->hello<!--]--><!--if--><!--]--><!--slot--></div>`,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('fragment slot (template v-if + multiple elements)', async () => {
|
test('fragment slot (template v-if + multiple elements)', async () => {
|
||||||
|
@ -106,7 +108,7 @@ describe('ssr: slot', () => {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(
|
).toBe(
|
||||||
`<div><!--[--><!--[--><div>one</div><div>two</div><!--]--><!--if--><!--]--></div>`,
|
`<div><!--[--><!--[--><div>one</div><div>two</div><!--]--><!--if--><!--]--><!--slot--></div>`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -171,7 +173,7 @@ describe('ssr: slot', () => {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(
|
).toBe(
|
||||||
`<button><!--[--><div><!--[--><!--]--></div><!--]--></button><!--dynamic-component-->`,
|
`<button><!--[--><div><!--[--><!--]--><!--slot--></div><!--]--></button><!--dynamic-component-->`,
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -189,7 +191,7 @@ describe('ssr: slot', () => {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toBe(
|
).toBe(
|
||||||
`<button><!--[--><div><!--[--><div>hello</div><!--]--></div><!--]--></button><!--dynamic-component-->`,
|
`<button><!--[--><div><!--[--><div>hello</div><!--]--><!--slot--></div><!--]--></button><!--dynamic-component-->`,
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
type SSRBufferItem,
|
type SSRBufferItem,
|
||||||
renderVNodeChildren,
|
renderVNodeChildren,
|
||||||
} from '../render'
|
} from '../render'
|
||||||
import { isArray } from '@vue/shared'
|
import { SLOT_ANCHOR_LABEL, isArray } from '@vue/shared'
|
||||||
|
|
||||||
const { ensureValidVNode } = ssrUtils
|
const { ensureValidVNode } = ssrUtils
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ export function ssrRenderSlot(
|
||||||
parentComponent,
|
parentComponent,
|
||||||
slotScopeId,
|
slotScopeId,
|
||||||
)
|
)
|
||||||
push(`<!--]-->`)
|
push(`<!--]--><!--${SLOT_ANCHOR_LABEL}-->`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ssrRenderSlotInner(
|
export function ssrRenderSlotInner(
|
||||||
|
|
Loading…
Reference in New Issue