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,
|
Transition,
|
||||||
watchEffect,
|
watchEffect,
|
||||||
createVNode,
|
createVNode,
|
||||||
resolveDynamicComponent
|
resolveDynamicComponent,
|
||||||
|
renderSlot
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { escapeHtml } from '@vue/shared'
|
import { escapeHtml } from '@vue/shared'
|
||||||
import { renderToString } from '../src/renderToString'
|
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>`)
|
expect(await render(h(Foo))).toBe(`<div data-v-test></div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('with slots', async () => {
|
test('with client-compiled vnode slots', async () => {
|
||||||
const Child = {
|
const Child = {
|
||||||
__scopeId: 'data-v-child',
|
__scopeId: 'data-v-child',
|
||||||
render: function(this: any) {
|
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',
|
__scopeId: 'data-v-test',
|
||||||
render: () => {
|
render: () => {
|
||||||
return h(Child, null, {
|
return h(Child, null, {
|
||||||
default: withCtx(() => h('span', 'slot'))
|
default: withCtx(() => [h('span', 'slot')])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(await render(h(Parent))).toBe(
|
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,
|
fallbackRenderFn: (() => void) | null,
|
||||||
push: PushFn,
|
push: PushFn,
|
||||||
parentComponent: ComponentInternalInstance,
|
parentComponent: ComponentInternalInstance,
|
||||||
slotScopeId?: string | null
|
slotScopeId?: string
|
||||||
) {
|
) {
|
||||||
// template-compiled slots are always rendered as fragments
|
// template-compiled slots are always rendered as fragments
|
||||||
push(`<!--[-->`)
|
push(`<!--[-->`)
|
||||||
@ -34,7 +34,7 @@ export function ssrRenderSlot(
|
|||||||
)
|
)
|
||||||
if (Array.isArray(ret)) {
|
if (Array.isArray(ret)) {
|
||||||
// normal slot
|
// normal slot
|
||||||
renderVNodeChildren(push, ret, parentComponent)
|
renderVNodeChildren(push, ret, parentComponent, slotScopeId)
|
||||||
} else {
|
} else {
|
||||||
// ssr slot.
|
// ssr slot.
|
||||||
// check if the slot renders all comments, in which case use the fallback
|
// check if the slot renders all comments, in which case use the fallback
|
||||||
|
@ -111,7 +111,8 @@ function renderComponentSubTree(
|
|||||||
renderVNode(
|
renderVNode(
|
||||||
push,
|
push,
|
||||||
(instance.subTree = renderComponentRoot(instance)),
|
(instance.subTree = renderComponentRoot(instance)),
|
||||||
instance
|
instance,
|
||||||
|
slotScopeId
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
if (
|
if (
|
||||||
@ -174,7 +175,8 @@ function renderComponentSubTree(
|
|||||||
renderVNode(
|
renderVNode(
|
||||||
push,
|
push,
|
||||||
(instance.subTree = renderComponentRoot(instance)),
|
(instance.subTree = renderComponentRoot(instance)),
|
||||||
instance
|
instance,
|
||||||
|
slotScopeId
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
warn(
|
warn(
|
||||||
@ -191,7 +193,8 @@ function renderComponentSubTree(
|
|||||||
export function renderVNode(
|
export function renderVNode(
|
||||||
push: PushFn,
|
push: PushFn,
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
parentComponent: ComponentInternalInstance
|
parentComponent: ComponentInternalInstance,
|
||||||
|
slotScopeId?: string
|
||||||
) {
|
) {
|
||||||
const { type, shapeFlag, children } = vnode
|
const { type, shapeFlag, children } = vnode
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -207,19 +210,28 @@ export function renderVNode(
|
|||||||
push(children as string)
|
push(children as string)
|
||||||
break
|
break
|
||||||
case Fragment:
|
case Fragment:
|
||||||
|
if (vnode.slotScopeIds) {
|
||||||
|
slotScopeId =
|
||||||
|
(slotScopeId ? slotScopeId + ' ' : '') + vnode.slotScopeIds.join(' ')
|
||||||
|
}
|
||||||
push(`<!--[-->`) // open
|
push(`<!--[-->`) // open
|
||||||
renderVNodeChildren(push, children as VNodeArrayChildren, parentComponent)
|
renderVNodeChildren(
|
||||||
|
push,
|
||||||
|
children as VNodeArrayChildren,
|
||||||
|
parentComponent,
|
||||||
|
slotScopeId
|
||||||
|
)
|
||||||
push(`<!--]-->`) // close
|
push(`<!--]-->`) // close
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
if (shapeFlag & ShapeFlags.ELEMENT) {
|
if (shapeFlag & ShapeFlags.ELEMENT) {
|
||||||
renderElementVNode(push, vnode, parentComponent)
|
renderElementVNode(push, vnode, parentComponent, slotScopeId)
|
||||||
} else if (shapeFlag & ShapeFlags.COMPONENT) {
|
} else if (shapeFlag & ShapeFlags.COMPONENT) {
|
||||||
push(renderComponentVNode(vnode, parentComponent))
|
push(renderComponentVNode(vnode, parentComponent, slotScopeId))
|
||||||
} else if (shapeFlag & ShapeFlags.TELEPORT) {
|
} else if (shapeFlag & ShapeFlags.TELEPORT) {
|
||||||
renderTeleportVNode(push, vnode, parentComponent)
|
renderTeleportVNode(push, vnode, parentComponent, slotScopeId)
|
||||||
} else if (shapeFlag & ShapeFlags.SUSPENSE) {
|
} else if (shapeFlag & ShapeFlags.SUSPENSE) {
|
||||||
renderVNode(push, vnode.ssContent!, parentComponent)
|
renderVNode(push, vnode.ssContent!, parentComponent, slotScopeId)
|
||||||
} else {
|
} else {
|
||||||
warn(
|
warn(
|
||||||
'[@vue/server-renderer] Invalid VNode type:',
|
'[@vue/server-renderer] Invalid VNode type:',
|
||||||
@ -233,17 +245,19 @@ export function renderVNode(
|
|||||||
export function renderVNodeChildren(
|
export function renderVNodeChildren(
|
||||||
push: PushFn,
|
push: PushFn,
|
||||||
children: VNodeArrayChildren,
|
children: VNodeArrayChildren,
|
||||||
parentComponent: ComponentInternalInstance
|
parentComponent: ComponentInternalInstance,
|
||||||
|
slotScopeId: string | undefined
|
||||||
) {
|
) {
|
||||||
for (let i = 0; i < children.length; i++) {
|
for (let i = 0; i < children.length; i++) {
|
||||||
renderVNode(push, normalizeVNode(children[i]), parentComponent)
|
renderVNode(push, normalizeVNode(children[i]), parentComponent, slotScopeId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderElementVNode(
|
function renderElementVNode(
|
||||||
push: PushFn,
|
push: PushFn,
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
parentComponent: ComponentInternalInstance
|
parentComponent: ComponentInternalInstance,
|
||||||
|
slotScopeId: string | undefined
|
||||||
) {
|
) {
|
||||||
const tag = vnode.type as string
|
const tag = vnode.type as string
|
||||||
let { props, children, shapeFlag, scopeId, dirs } = vnode
|
let { props, children, shapeFlag, scopeId, dirs } = vnode
|
||||||
@ -257,7 +271,22 @@ function renderElementVNode(
|
|||||||
openTag += ssrRenderAttrs(props, tag)
|
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 + `>`)
|
push(openTag + `>`)
|
||||||
if (!isVoidTag(tag)) {
|
if (!isVoidTag(tag)) {
|
||||||
@ -281,7 +310,8 @@ function renderElementVNode(
|
|||||||
renderVNodeChildren(
|
renderVNodeChildren(
|
||||||
push,
|
push,
|
||||||
children as VNodeArrayChildren,
|
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(
|
function applySSRDirectives(
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
rawProps: VNodeProps | null,
|
rawProps: VNodeProps | null,
|
||||||
@ -338,7 +341,8 @@ function applySSRDirectives(
|
|||||||
function renderTeleportVNode(
|
function renderTeleportVNode(
|
||||||
push: PushFn,
|
push: PushFn,
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
parentComponent: ComponentInternalInstance
|
parentComponent: ComponentInternalInstance,
|
||||||
|
slotScopeId: string | undefined
|
||||||
) {
|
) {
|
||||||
const target = vnode.props && vnode.props.to
|
const target = vnode.props && vnode.props.to
|
||||||
const disabled = vnode.props && vnode.props.disabled
|
const disabled = vnode.props && vnode.props.disabled
|
||||||
@ -358,7 +362,8 @@ function renderTeleportVNode(
|
|||||||
renderVNodeChildren(
|
renderVNodeChildren(
|
||||||
push,
|
push,
|
||||||
vnode.children as VNodeArrayChildren,
|
vnode.children as VNodeArrayChildren,
|
||||||
parentComponent
|
parentComponent,
|
||||||
|
slotScopeId
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
target,
|
target,
|
||||||
|
Loading…
Reference in New Issue
Block a user