wip(ssr): renderer support for optimized and manual slots
This commit is contained in:
parent
a7b0954f14
commit
6b1ce00621
@ -12,7 +12,7 @@ import {
|
|||||||
TestElement,
|
TestElement,
|
||||||
TestNode
|
TestNode
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
import { VNodeChildren } from '../src/vnode'
|
import { VNodeArrayChildren } from '../src/vnode'
|
||||||
|
|
||||||
describe('renderer: portal', () => {
|
describe('renderer: portal', () => {
|
||||||
test('should work', () => {
|
test('should work', () => {
|
||||||
@ -60,7 +60,7 @@ describe('renderer: portal', () => {
|
|||||||
test('should update children', async () => {
|
test('should update children', async () => {
|
||||||
const target = nodeOps.createElement('div')
|
const target = nodeOps.createElement('div')
|
||||||
const root = nodeOps.createElement('div')
|
const root = nodeOps.createElement('div')
|
||||||
const children = ref<VNodeChildren<TestNode, TestElement>>([
|
const children = ref<VNodeArrayChildren<TestNode, TestElement>>([
|
||||||
h('div', 'teleported')
|
h('div', 'teleported')
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import { ComponentInternalInstance, currentInstance } from './component'
|
import { ComponentInternalInstance, currentInstance } from './component'
|
||||||
import { VNode, NormalizedChildren, normalizeVNode, VNodeChild } from './vnode'
|
import {
|
||||||
|
VNode,
|
||||||
|
VNodeNormalizedChildren,
|
||||||
|
normalizeVNode,
|
||||||
|
VNodeChild
|
||||||
|
} from './vnode'
|
||||||
import { isArray, isFunction, EMPTY_OBJ } from '@vue/shared'
|
import { isArray, isFunction, EMPTY_OBJ } from '@vue/shared'
|
||||||
import { ShapeFlags } from './shapeFlags'
|
import { ShapeFlags } from './shapeFlags'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
@ -41,7 +46,7 @@ const normalizeSlot = (key: string, rawSlot: Function): Slot => (
|
|||||||
|
|
||||||
export function resolveSlots(
|
export function resolveSlots(
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
children: NormalizedChildren
|
children: VNodeNormalizedChildren
|
||||||
) {
|
) {
|
||||||
let slots: InternalSlots | void
|
let slots: InternalSlots | void
|
||||||
if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
|
if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
Comment,
|
Comment,
|
||||||
isSameVNodeType,
|
isSameVNodeType,
|
||||||
VNode,
|
VNode,
|
||||||
VNodeChildren
|
VNodeArrayChildren
|
||||||
} from '../vnode'
|
} from '../vnode'
|
||||||
import { warn } from '../warning'
|
import { warn } from '../warning'
|
||||||
import { isKeepAlive } from './KeepAlive'
|
import { isKeepAlive } from './KeepAlive'
|
||||||
@ -370,7 +370,7 @@ function emptyPlaceholder(vnode: VNode): VNode | undefined {
|
|||||||
function getKeepAliveChild(vnode: VNode): VNode | undefined {
|
function getKeepAliveChild(vnode: VNode): VNode | undefined {
|
||||||
return isKeepAlive(vnode)
|
return isKeepAlive(vnode)
|
||||||
? vnode.children
|
? vnode.children
|
||||||
? ((vnode.children as VNodeChildren)[0] as VNode)
|
? ((vnode.children as VNodeArrayChildren)[0] as VNode)
|
||||||
: undefined
|
: undefined
|
||||||
: vnode
|
: vnode
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import {
|
|||||||
VNode,
|
VNode,
|
||||||
VNodeProps,
|
VNodeProps,
|
||||||
createVNode,
|
createVNode,
|
||||||
VNodeChildren,
|
VNodeArrayChildren,
|
||||||
Fragment,
|
Fragment,
|
||||||
Portal,
|
Portal,
|
||||||
isVNode
|
isVNode
|
||||||
@ -62,7 +62,7 @@ type RawChildren =
|
|||||||
| number
|
| number
|
||||||
| boolean
|
| boolean
|
||||||
| VNode
|
| VNode
|
||||||
| VNodeChildren
|
| VNodeArrayChildren
|
||||||
| (() => any)
|
| (() => any)
|
||||||
|
|
||||||
// fake constructor type returned from `defineComponent`
|
// fake constructor type returned from `defineComponent`
|
||||||
@ -85,11 +85,11 @@ export function h(
|
|||||||
): VNode
|
): VNode
|
||||||
|
|
||||||
// fragment
|
// fragment
|
||||||
export function h(type: typeof Fragment, children?: VNodeChildren): VNode
|
export function h(type: typeof Fragment, children?: VNodeArrayChildren): VNode
|
||||||
export function h(
|
export function h(
|
||||||
type: typeof Fragment,
|
type: typeof Fragment,
|
||||||
props?: RawProps | null,
|
props?: RawProps | null,
|
||||||
children?: VNodeChildren
|
children?: VNodeArrayChildren
|
||||||
): VNode
|
): VNode
|
||||||
|
|
||||||
// portal (target prop is required)
|
// portal (target prop is required)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Data } from '../component'
|
import { Data } from '../component'
|
||||||
import { Slot } from '../componentSlots'
|
import { Slot } from '../componentSlots'
|
||||||
import {
|
import {
|
||||||
VNodeChildren,
|
VNodeArrayChildren,
|
||||||
openBlock,
|
openBlock,
|
||||||
createBlock,
|
createBlock,
|
||||||
Fragment,
|
Fragment,
|
||||||
@ -15,7 +15,7 @@ export function renderSlot(
|
|||||||
props: Data = {},
|
props: Data = {},
|
||||||
// this is not a user-facing function, so the fallback is always generated by
|
// this is not a user-facing function, so the fallback is always generated by
|
||||||
// the compiler and guaranteed to be an array
|
// the compiler and guaranteed to be an array
|
||||||
fallback?: VNodeChildren
|
fallback?: VNodeArrayChildren
|
||||||
): VNode {
|
): VNode {
|
||||||
const slot = slots[name]
|
const slot = slots[name]
|
||||||
return (
|
return (
|
||||||
|
@ -125,7 +125,13 @@ export {
|
|||||||
Plugin,
|
Plugin,
|
||||||
CreateAppFunction
|
CreateAppFunction
|
||||||
} from './apiCreateApp'
|
} from './apiCreateApp'
|
||||||
export { VNode, VNodeTypes, VNodeProps, VNodeChildren } from './vnode'
|
export {
|
||||||
|
VNode,
|
||||||
|
VNodeTypes,
|
||||||
|
VNodeProps,
|
||||||
|
VNodeArrayChildren,
|
||||||
|
VNodeNormalizedChildren
|
||||||
|
} from './vnode'
|
||||||
export {
|
export {
|
||||||
Component,
|
Component,
|
||||||
FunctionalComponent,
|
FunctionalComponent,
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
cloneIfMounted,
|
cloneIfMounted,
|
||||||
normalizeVNode,
|
normalizeVNode,
|
||||||
VNode,
|
VNode,
|
||||||
VNodeChildren,
|
VNodeArrayChildren,
|
||||||
createVNode,
|
createVNode,
|
||||||
isSameVNodeType
|
isSameVNodeType
|
||||||
} from './vnode'
|
} from './vnode'
|
||||||
@ -177,7 +177,7 @@ export function createRenderer<
|
|||||||
createApp: CreateAppFunction<HostElement>
|
createApp: CreateAppFunction<HostElement>
|
||||||
} {
|
} {
|
||||||
type HostVNode = VNode<HostNode, HostElement>
|
type HostVNode = VNode<HostNode, HostElement>
|
||||||
type HostVNodeChildren = VNodeChildren<HostNode, HostElement>
|
type HostVNodeChildren = VNodeArrayChildren<HostNode, HostElement>
|
||||||
type HostSuspenseBoundary = SuspenseBoundary<HostNode, HostElement>
|
type HostSuspenseBoundary = SuspenseBoundary<HostNode, HostElement>
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -71,19 +71,19 @@ type VNodeChildAtom<HostNode, HostElement> =
|
|||||||
| null
|
| null
|
||||||
| void
|
| void
|
||||||
|
|
||||||
export interface VNodeChildren<HostNode = any, HostElement = any>
|
export interface VNodeArrayChildren<HostNode = any, HostElement = any>
|
||||||
extends Array<
|
extends Array<
|
||||||
| VNodeChildren<HostNode, HostElement>
|
| VNodeArrayChildren<HostNode, HostElement>
|
||||||
| VNodeChildAtom<HostNode, HostElement>
|
| VNodeChildAtom<HostNode, HostElement>
|
||||||
> {}
|
> {}
|
||||||
|
|
||||||
export type VNodeChild<HostNode = any, HostElement = any> =
|
export type VNodeChild<HostNode = any, HostElement = any> =
|
||||||
| VNodeChildAtom<HostNode, HostElement>
|
| VNodeChildAtom<HostNode, HostElement>
|
||||||
| VNodeChildren<HostNode, HostElement>
|
| VNodeArrayChildren<HostNode, HostElement>
|
||||||
|
|
||||||
export type NormalizedChildren<HostNode = any, HostElement = any> =
|
export type VNodeNormalizedChildren<HostNode = any, HostElement = any> =
|
||||||
| string
|
| string
|
||||||
| VNodeChildren<HostNode, HostElement>
|
| VNodeArrayChildren<HostNode, HostElement>
|
||||||
| RawSlots
|
| RawSlots
|
||||||
| null
|
| null
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ export interface VNode<HostNode = any, HostElement = any> {
|
|||||||
key: string | number | null
|
key: string | number | null
|
||||||
ref: string | Ref | ((ref: object | null) => void) | null
|
ref: string | Ref | ((ref: object | null) => void) | null
|
||||||
scopeId: string | null // SFC only
|
scopeId: string | null // SFC only
|
||||||
children: NormalizedChildren<HostNode, HostElement>
|
children: VNodeNormalizedChildren<HostNode, HostElement>
|
||||||
component: ComponentInternalInstance | null
|
component: ComponentInternalInstance | null
|
||||||
suspense: SuspenseBoundary<HostNode, HostElement> | null
|
suspense: SuspenseBoundary<HostNode, HostElement> | null
|
||||||
dirs: DirectiveBinding[] | null
|
dirs: DirectiveBinding[] | null
|
||||||
@ -376,7 +376,7 @@ export function normalizeChildren(vnode: VNode, children: unknown) {
|
|||||||
children = String(children)
|
children = String(children)
|
||||||
type = ShapeFlags.TEXT_CHILDREN
|
type = ShapeFlags.TEXT_CHILDREN
|
||||||
}
|
}
|
||||||
vnode.children = children as NormalizedChildren
|
vnode.children = children as VNodeNormalizedChildren
|
||||||
vnode.shapeFlag |= type
|
vnode.shapeFlag |= type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,21 +3,24 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
ComponentInternalInstance,
|
ComponentInternalInstance,
|
||||||
VNode,
|
VNode,
|
||||||
VNodeChildren,
|
VNodeArrayChildren,
|
||||||
|
VNodeNormalizedChildren,
|
||||||
createVNode,
|
createVNode,
|
||||||
Text,
|
Text,
|
||||||
Comment,
|
Comment,
|
||||||
Fragment,
|
Fragment,
|
||||||
Portal,
|
Portal,
|
||||||
ShapeFlags,
|
ShapeFlags,
|
||||||
ssrUtils
|
ssrUtils,
|
||||||
|
Slot
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import {
|
import {
|
||||||
isString,
|
isString,
|
||||||
isPromise,
|
isPromise,
|
||||||
isArray,
|
isArray,
|
||||||
isFunction,
|
isFunction,
|
||||||
isVoidTag
|
isVoidTag,
|
||||||
|
EMPTY_OBJ
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import { renderProps } from './renderProps'
|
import { renderProps } from './renderProps'
|
||||||
import { escape } from './escape'
|
import { escape } from './escape'
|
||||||
@ -39,6 +42,7 @@ type SSRBuffer = SSRBufferItem[]
|
|||||||
type SSRBufferItem = string | ResolvedSSRBuffer | Promise<ResolvedSSRBuffer>
|
type SSRBufferItem = string | ResolvedSSRBuffer | Promise<ResolvedSSRBuffer>
|
||||||
type ResolvedSSRBuffer = (string | ResolvedSSRBuffer)[]
|
type ResolvedSSRBuffer = (string | ResolvedSSRBuffer)[]
|
||||||
type PushFn = (item: SSRBufferItem) => void
|
type PushFn = (item: SSRBufferItem) => void
|
||||||
|
type Props = Record<string, unknown>
|
||||||
|
|
||||||
function createBuffer() {
|
function createBuffer() {
|
||||||
let appendable = false
|
let appendable = false
|
||||||
@ -79,26 +83,36 @@ function unrollBuffer(buffer: ResolvedSSRBuffer): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function renderToString(app: App): Promise<string> {
|
export async function renderToString(app: App): Promise<string> {
|
||||||
const resolvedBuffer = await renderComponent(
|
const resolvedBuffer = await renderComponent(app._component, app._props, null)
|
||||||
createVNode(app._component, app._props)
|
|
||||||
)
|
|
||||||
return unrollBuffer(resolvedBuffer)
|
return unrollBuffer(resolvedBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderComponent(
|
export function renderComponent(
|
||||||
|
comp: Component,
|
||||||
|
props: Props | null,
|
||||||
|
children: VNodeNormalizedChildren | null,
|
||||||
|
parentComponent: ComponentInternalInstance | null = null
|
||||||
|
): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
|
||||||
|
return renderComponentVNode(
|
||||||
|
createVNode(comp, props, children),
|
||||||
|
parentComponent
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderComponentVNode(
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
parentComponent: ComponentInternalInstance | null = null
|
parentComponent: ComponentInternalInstance | null = null
|
||||||
): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
|
): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
|
||||||
const instance = createComponentInstance(vnode, parentComponent)
|
const instance = createComponentInstance(vnode, parentComponent)
|
||||||
const res = setupComponent(instance, null)
|
const res = setupComponent(instance, null)
|
||||||
if (isPromise(res)) {
|
if (isPromise(res)) {
|
||||||
return res.then(() => innerRenderComponent(instance))
|
return res.then(() => renderComponentSubTree(instance))
|
||||||
} else {
|
} else {
|
||||||
return innerRenderComponent(instance)
|
return renderComponentSubTree(instance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function innerRenderComponent(
|
function renderComponentSubTree(
|
||||||
instance: ComponentInternalInstance
|
instance: ComponentInternalInstance
|
||||||
): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
|
): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
|
||||||
const comp = instance.type as Component
|
const comp = instance.type as Component
|
||||||
@ -141,7 +155,7 @@ export function renderVNode(
|
|||||||
break
|
break
|
||||||
case Fragment:
|
case Fragment:
|
||||||
push(`<!---->`)
|
push(`<!---->`)
|
||||||
renderVNodeChildren(push, children as VNodeChildren, parentComponent)
|
renderVNodeChildren(push, children as VNodeArrayChildren, parentComponent)
|
||||||
push(`<!---->`)
|
push(`<!---->`)
|
||||||
break
|
break
|
||||||
case Portal:
|
case Portal:
|
||||||
@ -151,7 +165,7 @@ export function renderVNode(
|
|||||||
if (shapeFlag & ShapeFlags.ELEMENT) {
|
if (shapeFlag & ShapeFlags.ELEMENT) {
|
||||||
renderElement(push, vnode, parentComponent)
|
renderElement(push, vnode, parentComponent)
|
||||||
} else if (shapeFlag & ShapeFlags.COMPONENT) {
|
} else if (shapeFlag & ShapeFlags.COMPONENT) {
|
||||||
push(renderComponent(vnode, parentComponent))
|
push(renderComponentVNode(vnode, parentComponent))
|
||||||
} else if (shapeFlag & ShapeFlags.SUSPENSE) {
|
} else if (shapeFlag & ShapeFlags.SUSPENSE) {
|
||||||
// TODO
|
// TODO
|
||||||
} else {
|
} else {
|
||||||
@ -166,7 +180,7 @@ export function renderVNode(
|
|||||||
|
|
||||||
function renderVNodeChildren(
|
function renderVNodeChildren(
|
||||||
push: PushFn,
|
push: PushFn,
|
||||||
children: VNodeChildren,
|
children: VNodeArrayChildren,
|
||||||
parentComponent: ComponentInternalInstance | null = null
|
parentComponent: ComponentInternalInstance | null = null
|
||||||
) {
|
) {
|
||||||
for (let i = 0; i < children.length; i++) {
|
for (let i = 0; i < children.length; i++) {
|
||||||
@ -218,13 +232,37 @@ function renderElement(
|
|||||||
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
|
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
|
||||||
push(escape(children as string))
|
push(escape(children as string))
|
||||||
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
|
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
|
||||||
renderVNodeChildren(push, children as VNodeChildren, parentComponent)
|
renderVNodeChildren(
|
||||||
|
push,
|
||||||
|
children as VNodeArrayChildren,
|
||||||
|
parentComponent
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
push(`</${tag}>`)
|
push(`</${tag}>`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderSlot() {
|
type OptimizedSlotFn = (
|
||||||
// TODO
|
props: Props,
|
||||||
|
push: PushFn,
|
||||||
|
parentComponent: ComponentInternalInstance | null
|
||||||
|
) => void
|
||||||
|
|
||||||
|
export function renderSlot(
|
||||||
|
slotFn: Slot | OptimizedSlotFn,
|
||||||
|
slotProps: Props,
|
||||||
|
push: PushFn,
|
||||||
|
parentComponent: ComponentInternalInstance | null = null
|
||||||
|
) {
|
||||||
|
// template-compiled slots are always rendered as fragments
|
||||||
|
push(`<!---->`)
|
||||||
|
if (slotFn.length > 2) {
|
||||||
|
// only ssr-optimized slot fns accept 3 arguments
|
||||||
|
slotFn(slotProps, push, parentComponent)
|
||||||
|
} else {
|
||||||
|
// normal slot
|
||||||
|
renderVNodeChildren(push, (slotFn as Slot)(slotProps), parentComponent)
|
||||||
|
}
|
||||||
|
push(`<!---->`)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user