Skip to content

Commit 58e2376

Browse files
committed
refactor(ssr): adjust ssr scope id logic for client-compiled render functions
1 parent 5e54081 commit 58e2376

File tree

3 files changed

+57
-49
lines changed

3 files changed

+57
-49
lines changed

packages/server-renderer/__tests__/render.spec.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import {
1313
Transition,
1414
watchEffect,
1515
createVNode,
16-
resolveDynamicComponent
16+
resolveDynamicComponent,
17+
renderSlot
1718
} from 'vue'
1819
import { escapeHtml } from '@vue/shared'
1920
import { renderToString } from '../src/renderToString'
@@ -711,25 +712,27 @@ function testRender(type: string, render: typeof renderToString) {
711712
expect(await render(h(Foo))).toBe(`<div data-v-test></div>`)
712713
})
713714

714-
test('with slots', async () => {
715+
test('with client-compiled vnode slots', async () => {
715716
const Child = {
716717
__scopeId: 'data-v-child',
717718
render: function(this: any) {
718-
return h('div', this.$slots.default())
719+
return h('div', null, [renderSlot(this.$slots, 'default')])
719720
}
720721
}
721722

722723
const Parent = {
723724
__scopeId: 'data-v-test',
724725
render: () => {
725726
return h(Child, null, {
726-
default: withCtx(() => h('span', 'slot'))
727+
default: withCtx(() => [h('span', 'slot')])
727728
})
728729
}
729730
}
730731

731732
expect(await render(h(Parent))).toBe(
732-
`<div data-v-child data-v-test><span data-v-test data-v-child-s>slot</span></div>`
733+
`<div data-v-child data-v-test>` +
734+
`<!--[--><span data-v-test data-v-child-s>slot</span><!--]-->` +
735+
`</div>`
733736
)
734737
})
735738
})

packages/server-renderer/src/helpers/ssrRenderSlot.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export function ssrRenderSlot(
1616
fallbackRenderFn: (() => void) | null,
1717
push: PushFn,
1818
parentComponent: ComponentInternalInstance,
19-
slotScopeId?: string | null
19+
slotScopeId?: string
2020
) {
2121
// template-compiled slots are always rendered as fragments
2222
push(`<!--[-->`)
@@ -34,7 +34,7 @@ export function ssrRenderSlot(
3434
)
3535
if (Array.isArray(ret)) {
3636
// normal slot
37-
renderVNodeChildren(push, ret, parentComponent)
37+
renderVNodeChildren(push, ret, parentComponent, slotScopeId)
3838
} else {
3939
// ssr slot.
4040
// check if the slot renders all comments, in which case use the fallback

packages/server-renderer/src/render.ts

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ function renderComponentSubTree(
111111
renderVNode(
112112
push,
113113
(instance.subTree = renderComponentRoot(instance)),
114-
instance
114+
instance,
115+
slotScopeId
115116
)
116117
} else {
117118
if (
@@ -174,7 +175,8 @@ function renderComponentSubTree(
174175
renderVNode(
175176
push,
176177
(instance.subTree = renderComponentRoot(instance)),
177-
instance
178+
instance,
179+
slotScopeId
178180
)
179181
} else {
180182
warn(
@@ -191,7 +193,8 @@ function renderComponentSubTree(
191193
export function renderVNode(
192194
push: PushFn,
193195
vnode: VNode,
194-
parentComponent: ComponentInternalInstance
196+
parentComponent: ComponentInternalInstance,
197+
slotScopeId?: string
195198
) {
196199
const { type, shapeFlag, children } = vnode
197200
switch (type) {
@@ -207,19 +210,28 @@ export function renderVNode(
207210
push(children as string)
208211
break
209212
case Fragment:
213+
if (vnode.slotScopeIds) {
214+
slotScopeId =
215+
(slotScopeId ? slotScopeId + ' ' : '') + vnode.slotScopeIds.join(' ')
216+
}
210217
push(`<!--[-->`) // open
211-
renderVNodeChildren(push, children as VNodeArrayChildren, parentComponent)
218+
renderVNodeChildren(
219+
push,
220+
children as VNodeArrayChildren,
221+
parentComponent,
222+
slotScopeId
223+
)
212224
push(`<!--]-->`) // close
213225
break
214226
default:
215227
if (shapeFlag & ShapeFlags.ELEMENT) {
216-
renderElementVNode(push, vnode, parentComponent)
228+
renderElementVNode(push, vnode, parentComponent, slotScopeId)
217229
} else if (shapeFlag & ShapeFlags.COMPONENT) {
218-
push(renderComponentVNode(vnode, parentComponent))
230+
push(renderComponentVNode(vnode, parentComponent, slotScopeId))
219231
} else if (shapeFlag & ShapeFlags.TELEPORT) {
220-
renderTeleportVNode(push, vnode, parentComponent)
232+
renderTeleportVNode(push, vnode, parentComponent, slotScopeId)
221233
} else if (shapeFlag & ShapeFlags.SUSPENSE) {
222-
renderVNode(push, vnode.ssContent!, parentComponent)
234+
renderVNode(push, vnode.ssContent!, parentComponent, slotScopeId)
223235
} else {
224236
warn(
225237
'[@vue/server-renderer] Invalid VNode type:',
@@ -233,17 +245,19 @@ export function renderVNode(
233245
export function renderVNodeChildren(
234246
push: PushFn,
235247
children: VNodeArrayChildren,
236-
parentComponent: ComponentInternalInstance
248+
parentComponent: ComponentInternalInstance,
249+
slotScopeId: string | undefined
237250
) {
238251
for (let i = 0; i < children.length; i++) {
239-
renderVNode(push, normalizeVNode(children[i]), parentComponent)
252+
renderVNode(push, normalizeVNode(children[i]), parentComponent, slotScopeId)
240253
}
241254
}
242255

243256
function renderElementVNode(
244257
push: PushFn,
245258
vnode: VNode,
246-
parentComponent: ComponentInternalInstance
259+
parentComponent: ComponentInternalInstance,
260+
slotScopeId: string | undefined
247261
) {
248262
const tag = vnode.type as string
249263
let { props, children, shapeFlag, scopeId, dirs } = vnode
@@ -257,7 +271,22 @@ function renderElementVNode(
257271
openTag += ssrRenderAttrs(props, tag)
258272
}
259273

260-
openTag += resolveScopeId(scopeId, vnode, parentComponent)
274+
if (scopeId) {
275+
openTag += ` ${scopeId}`
276+
}
277+
// inherit parent chain scope id if this is the root node
278+
let curParent: ComponentInternalInstance | null = parentComponent
279+
let curVnode = vnode
280+
while (curParent && curVnode === curParent.subTree) {
281+
curVnode = curParent.vnode
282+
if (curVnode.scopeId) {
283+
openTag += ` ${curVnode.scopeId}`
284+
}
285+
curParent = curParent.parent
286+
}
287+
if (slotScopeId) {
288+
openTag += ` ${slotScopeId}`
289+
}
261290

262291
push(openTag + `>`)
263292
if (!isVoidTag(tag)) {
@@ -281,41 +310,15 @@ function renderElementVNode(
281310
renderVNodeChildren(
282311
push,
283312
children as VNodeArrayChildren,
284-
parentComponent
313+
parentComponent,
314+
slotScopeId
285315
)
286316
}
287317
}
288318
push(`</${tag}>`)
289319
}
290320
}
291321

292-
function resolveScopeId(
293-
scopeId: string | null,
294-
vnode: VNode,
295-
parentComponent: ComponentInternalInstance | null
296-
) {
297-
let res = ``
298-
if (scopeId) {
299-
res = ` ${scopeId}`
300-
}
301-
if (parentComponent) {
302-
const treeOwnerId = parentComponent.type.__scopeId
303-
// vnode's own scopeId and the current rendering component's scopeId is
304-
// different - this is a slot content node.
305-
if (treeOwnerId && treeOwnerId !== scopeId) {
306-
res += ` ${treeOwnerId}-s`
307-
}
308-
if (vnode === parentComponent.subTree) {
309-
res += resolveScopeId(
310-
parentComponent.vnode.scopeId,
311-
parentComponent.vnode,
312-
parentComponent.parent
313-
)
314-
}
315-
}
316-
return res
317-
}
318-
319322
function applySSRDirectives(
320323
vnode: VNode,
321324
rawProps: VNodeProps | null,
@@ -338,7 +341,8 @@ function applySSRDirectives(
338341
function renderTeleportVNode(
339342
push: PushFn,
340343
vnode: VNode,
341-
parentComponent: ComponentInternalInstance
344+
parentComponent: ComponentInternalInstance,
345+
slotScopeId: string | undefined
342346
) {
343347
const target = vnode.props && vnode.props.to
344348
const disabled = vnode.props && vnode.props.disabled
@@ -358,7 +362,8 @@ function renderTeleportVNode(
358362
renderVNodeChildren(
359363
push,
360364
vnode.children as VNodeArrayChildren,
361-
parentComponent
365+
parentComponent,
366+
slotScopeId
362367
)
363368
},
364369
target,

0 commit comments

Comments
 (0)