refactor(ssr): adjust ssr scope id logic for client-compiled render functions
This commit is contained in:
parent
5e54081d5b
commit
58e2376c4d
@ -13,7 +13,8 @@ import {
|
||||
Transition,
|
||||
watchEffect,
|
||||
createVNode,
|
||||
resolveDynamicComponent
|
||||
resolveDynamicComponent,
|
||||
renderSlot
|
||||
} from 'vue'
|
||||
import { escapeHtml } from '@vue/shared'
|
||||
import { renderToString } from '../src/renderToString'
|
||||
@ -711,11 +712,11 @@ function testRender(type: string, render: typeof 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')])
|
||||
}
|
||||
}
|
||||
|
||||
@ -723,13 +724,15 @@ function testRender(type: string, render: typeof renderToString) {
|
||||
__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>`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -16,7 +16,7 @@ export function ssrRenderSlot(
|
||||
fallbackRenderFn: (() => void) | null,
|
||||
push: PushFn,
|
||||
parentComponent: ComponentInternalInstance,
|
||||
slotScopeId?: string | null
|
||||
slotScopeId?: string
|
||||
) {
|
||||
// template-compiled slots are always rendered as fragments
|
||||
push(`<!--[-->`)
|
||||
@ -34,7 +34,7 @@ export function ssrRenderSlot(
|
||||
)
|
||||
if (Array.isArray(ret)) {
|
||||
// normal slot
|
||||
renderVNodeChildren(push, ret, parentComponent)
|
||||
renderVNodeChildren(push, ret, parentComponent, slotScopeId)
|
||||
} else {
|
||||
// ssr slot.
|
||||
// check if the slot renders all comments, in which case use the fallback
|
||||
|
@ -111,7 +111,8 @@ function renderComponentSubTree(
|
||||
renderVNode(
|
||||
push,
|
||||
(instance.subTree = renderComponentRoot(instance)),
|
||||
instance
|
||||
instance,
|
||||
slotScopeId
|
||||
)
|
||||
} else {
|
||||
if (
|
||||
@ -174,7 +175,8 @@ function renderComponentSubTree(
|
||||
renderVNode(
|
||||
push,
|
||||
(instance.subTree = renderComponentRoot(instance)),
|
||||
instance
|
||||
instance,
|
||||
slotScopeId
|
||||
)
|
||||
} else {
|
||||
warn(
|
||||
@ -191,7 +193,8 @@ function renderComponentSubTree(
|
||||
export function renderVNode(
|
||||
push: PushFn,
|
||||
vnode: VNode,
|
||||
parentComponent: ComponentInternalInstance
|
||||
parentComponent: ComponentInternalInstance,
|
||||
slotScopeId?: string
|
||||
) {
|
||||
const { type, shapeFlag, children } = vnode
|
||||
switch (type) {
|
||||
@ -207,19 +210,28 @@ export function renderVNode(
|
||||
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:',
|
||||
@ -233,17 +245,19 @@ export function renderVNode(
|
||||
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
|
||||
@ -257,7 +271,22 @@ function renderElementVNode(
|
||||
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)) {
|
||||
@ -281,7 +310,8 @@ function renderElementVNode(
|
||||
renderVNodeChildren(
|
||||
push,
|
||||
children as VNodeArrayChildren,
|
||||
parentComponent
|
||||
parentComponent,
|
||||
slotScopeId
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -289,33 +319,6 @@ function renderElementVNode(
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@ -338,7 +341,8 @@ function applySSRDirectives(
|
||||
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
|
||||
@ -358,7 +362,8 @@ function renderTeleportVNode(
|
||||
renderVNodeChildren(
|
||||
push,
|
||||
vnode.children as VNodeArrayChildren,
|
||||
parentComponent
|
||||
parentComponent,
|
||||
slotScopeId
|
||||
)
|
||||
},
|
||||
target,
|
||||
|
Loading…
Reference in New Issue
Block a user