wip(srr): slot outlet

This commit is contained in:
Evan You
2020-02-05 21:04:40 -05:00
parent 7a63103a11
commit 9b3b6962df
14 changed files with 263 additions and 120 deletions

View File

@@ -7,11 +7,8 @@ import {
ComponentOptions
} from 'vue'
import { escapeHtml } from '@vue/shared'
import {
renderToString,
renderComponent,
renderSlot
} from '../src/renderToString'
import { renderToString, renderComponent } from '../src/renderToString'
import { renderSlot } from '../src/helpers/renderSlot'
describe('ssr: renderToString', () => {
test('should apply app context', async () => {
@@ -135,7 +132,16 @@ describe('ssr: renderToString', () => {
props: ['msg'],
ssrRender(ctx: any, push: any, parent: any) {
push(`<div class="child">`)
renderSlot(ctx.$slots.default, { msg: 'from slot' }, push, parent)
renderSlot(
ctx.$slots,
'default',
{ msg: 'from slot' },
() => {
push(`fallback`)
},
push,
parent
)
push(`</div>`)
}
}
@@ -169,6 +175,19 @@ describe('ssr: renderToString', () => {
`<!----><span>from slot</span><!---->` +
`</div></div>`
)
// test fallback
expect(
await renderToString(
createApp({
ssrRender(_ctx, push, parent) {
push(`<div>parent`)
push(renderComponent(Child, { msg: 'hello' }, null, parent))
push(`</div>`)
}
})
)
).toBe(`<div>parent<div class="child"><!---->fallback<!----></div></div>`)
})
test('nested components with vnode slots', async () => {
@@ -176,7 +195,14 @@ describe('ssr: renderToString', () => {
props: ['msg'],
ssrRender(ctx: any, push: any, parent: any) {
push(`<div class="child">`)
renderSlot(ctx.$slots.default, { msg: 'from slot' }, push, parent)
renderSlot(
ctx.$slots,
'default',
{ msg: 'from slot' },
null,
push,
parent
)
push(`</div>`)
}
}

View File

@@ -0,0 +1,35 @@
import { Props, PushFn, renderVNodeChildren } from '../renderToString'
import { ComponentInternalInstance, Slot, Slots } from 'vue'
export type SSRSlots = Record<string, SSRSlot>
export type SSRSlot = (
props: Props,
push: PushFn,
parentComponent: ComponentInternalInstance | null
) => void
export function renderSlot(
slots: Slots | SSRSlots,
slotName: string,
slotProps: Props,
fallbackRenderFn: (() => void) | null,
push: PushFn,
parentComponent: ComponentInternalInstance | null = null
) {
const slotFn = slots[slotName]
// template-compiled slots are always rendered as fragments
push(`<!---->`)
if (slotFn) {
if (slotFn.length > 1) {
// only ssr-optimized slot fns accept more than 1 arguments
slotFn(slotProps, push, parentComponent)
} else {
// normal slot
renderVNodeChildren(push, (slotFn as Slot)(slotProps), parentComponent)
}
} else if (fallbackRenderFn) {
fallbackRenderFn()
}
push(`<!---->`)
}

View File

@@ -2,10 +2,8 @@
export { renderToString } from './renderToString'
// internal runtime helpers
export {
renderComponent as _renderComponent,
renderSlot as _renderSlot
} from './renderToString'
export { renderComponent as _renderComponent } from './renderToString'
export { renderSlot as _renderSlot } from './helpers/renderSlot'
export {
renderClass as _renderClass,
renderStyle as _renderStyle,

View File

@@ -11,7 +11,6 @@ import {
Portal,
ShapeFlags,
ssrUtils,
Slot,
Slots
} from 'vue'
import {
@@ -23,6 +22,7 @@ import {
escapeHtml
} from '@vue/shared'
import { renderAttrs } from './helpers/renderAttrs'
import { SSRSlots } from './helpers/renderSlot'
const {
isVNode,
@@ -41,8 +41,8 @@ const {
type SSRBuffer = SSRBufferItem[]
type SSRBufferItem = string | ResolvedSSRBuffer | Promise<ResolvedSSRBuffer>
type ResolvedSSRBuffer = (string | ResolvedSSRBuffer)[]
type PushFn = (item: SSRBufferItem) => void
type Props = Record<string, unknown>
export type PushFn = (item: SSRBufferItem) => void
export type Props = Record<string, unknown>
function createBuffer() {
let appendable = false
@@ -191,7 +191,7 @@ function renderVNode(
}
}
function renderVNodeChildren(
export function renderVNodeChildren(
push: PushFn,
children: VNodeArrayChildren,
parentComponent: ComponentInternalInstance | null = null
@@ -255,29 +255,3 @@ function renderElement(
push(`</${tag}>`)
}
}
export type SSRSlots = Record<string, SSRSlot>
export type SSRSlot = (
props: Props,
push: PushFn,
parentComponent: ComponentInternalInstance | null
) => void
export function renderSlot(
slotFn: Slot | SSRSlot,
slotProps: Props,
push: PushFn,
parentComponent: ComponentInternalInstance | null = null
) {
// template-compiled slots are always rendered as fragments
push(`<!---->`)
if (slotFn.length > 1) {
// only ssr-optimized slot fns accept more than 1 arguments
slotFn(slotProps, push, parentComponent)
} else {
// normal slot
renderVNodeChildren(push, (slotFn as Slot)(slotProps), parentComponent)
}
push(`<!---->`)
}