Transition,
watchEffect,
createVNode,
- resolveDynamicComponent
+ resolveDynamicComponent,
+ renderSlot
} from 'vue'
import { escapeHtml } from '@vue/shared'
import { renderToString } from '../src/renderToString'
expect(await render(h(Foo))).toBe(`<div data-v-test></div>`)
})
- test('with slots', async () => {
+ test('with client-compiled vnode slots', async () => {
const Child = {
__scopeId: 'data-v-child',
render: function(this: any) {
- return h('div', this.$slots.default())
+ return h('div', null, [renderSlot(this.$slots, 'default')])
}
}
__scopeId: 'data-v-test',
render: () => {
return h(Child, null, {
- default: withCtx(() => h('span', 'slot'))
+ default: withCtx(() => [h('span', 'slot')])
})
}
}
expect(await render(h(Parent))).toBe(
- `<div data-v-child data-v-test><span data-v-test data-v-child-s>slot</span></div>`
+ `<div data-v-child data-v-test>` +
+ `<!--[--><span data-v-test data-v-child-s>slot</span><!--]-->` +
+ `</div>`
)
})
})
renderVNode(
push,
(instance.subTree = renderComponentRoot(instance)),
- instance
+ instance,
+ slotScopeId
)
} else {
if (
renderVNode(
push,
(instance.subTree = renderComponentRoot(instance)),
- instance
+ instance,
+ slotScopeId
)
} else {
warn(
export function renderVNode(
push: PushFn,
vnode: VNode,
- parentComponent: ComponentInternalInstance
+ parentComponent: ComponentInternalInstance,
+ slotScopeId?: string
) {
const { type, shapeFlag, children } = vnode
switch (type) {
push(children as string)
break
case Fragment:
+ if (vnode.slotScopeIds) {
+ slotScopeId =
+ (slotScopeId ? slotScopeId + ' ' : '') + vnode.slotScopeIds.join(' ')
+ }
push(`<!--[-->`) // open
- renderVNodeChildren(push, children as VNodeArrayChildren, parentComponent)
+ renderVNodeChildren(
+ push,
+ children as VNodeArrayChildren,
+ parentComponent,
+ slotScopeId
+ )
push(`<!--]-->`) // close
break
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
- renderElementVNode(push, vnode, parentComponent)
+ renderElementVNode(push, vnode, parentComponent, slotScopeId)
} else if (shapeFlag & ShapeFlags.COMPONENT) {
- push(renderComponentVNode(vnode, parentComponent))
+ push(renderComponentVNode(vnode, parentComponent, slotScopeId))
} else if (shapeFlag & ShapeFlags.TELEPORT) {
- renderTeleportVNode(push, vnode, parentComponent)
+ renderTeleportVNode(push, vnode, parentComponent, slotScopeId)
} else if (shapeFlag & ShapeFlags.SUSPENSE) {
- renderVNode(push, vnode.ssContent!, parentComponent)
+ renderVNode(push, vnode.ssContent!, parentComponent, slotScopeId)
} else {
warn(
'[@vue/server-renderer] Invalid VNode type:',
export function renderVNodeChildren(
push: PushFn,
children: VNodeArrayChildren,
- parentComponent: ComponentInternalInstance
+ parentComponent: ComponentInternalInstance,
+ slotScopeId: string | undefined
) {
for (let i = 0; i < children.length; i++) {
- renderVNode(push, normalizeVNode(children[i]), parentComponent)
+ renderVNode(push, normalizeVNode(children[i]), parentComponent, slotScopeId)
}
}
function renderElementVNode(
push: PushFn,
vnode: VNode,
- parentComponent: ComponentInternalInstance
+ parentComponent: ComponentInternalInstance,
+ slotScopeId: string | undefined
) {
const tag = vnode.type as string
let { props, children, shapeFlag, scopeId, dirs } = vnode
openTag += ssrRenderAttrs(props, tag)
}
- openTag += resolveScopeId(scopeId, vnode, parentComponent)
+ if (scopeId) {
+ openTag += ` ${scopeId}`
+ }
+ // inherit parent chain scope id if this is the root node
+ let curParent: ComponentInternalInstance | null = parentComponent
+ let curVnode = vnode
+ while (curParent && curVnode === curParent.subTree) {
+ curVnode = curParent.vnode
+ if (curVnode.scopeId) {
+ openTag += ` ${curVnode.scopeId}`
+ }
+ curParent = curParent.parent
+ }
+ if (slotScopeId) {
+ openTag += ` ${slotScopeId}`
+ }
push(openTag + `>`)
if (!isVoidTag(tag)) {
renderVNodeChildren(
push,
children as VNodeArrayChildren,
- parentComponent
+ parentComponent,
+ slotScopeId
)
}
}
}
}
-function resolveScopeId(
- scopeId: string | null,
- vnode: VNode,
- parentComponent: ComponentInternalInstance | null
-) {
- let res = ``
- if (scopeId) {
- res = ` ${scopeId}`
- }
- if (parentComponent) {
- const treeOwnerId = parentComponent.type.__scopeId
- // vnode's own scopeId and the current rendering component's scopeId is
- // different - this is a slot content node.
- if (treeOwnerId && treeOwnerId !== scopeId) {
- res += ` ${treeOwnerId}-s`
- }
- if (vnode === parentComponent.subTree) {
- res += resolveScopeId(
- parentComponent.vnode.scopeId,
- parentComponent.vnode,
- parentComponent.parent
- )
- }
- }
- return res
-}
-
function applySSRDirectives(
vnode: VNode,
rawProps: VNodeProps | null,
function renderTeleportVNode(
push: PushFn,
vnode: VNode,
- parentComponent: ComponentInternalInstance
+ parentComponent: ComponentInternalInstance,
+ slotScopeId: string | undefined
) {
const target = vnode.props && vnode.props.to
const disabled = vnode.props && vnode.props.disabled
renderVNodeChildren(
push,
vnode.children as VNodeArrayChildren,
- parentComponent
+ parentComponent,
+ slotScopeId
)
},
target,