Skip to content

Commit 3be3785

Browse files
committed
fix(ssr): fix ssr on-the-fly compilation + slot fallback branch helper injection
1 parent 274f81c commit 3be3785

File tree

6 files changed

+63
-51
lines changed

6 files changed

+63
-51
lines changed

packages/compiler-ssr/__tests__/ssrComponent.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describe('ssr: components', () => {
7070
test('explicit default slot', () => {
7171
expect(compile(`<foo v-slot="{ msg }">{{ msg + outer }}</foo>`).code)
7272
.toMatchInlineSnapshot(`
73-
"const { resolveComponent: _resolveComponent, createTextVNode: _createTextVNode } = require(\\"vue\\")
73+
"const { resolveComponent: _resolveComponent, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode } = require(\\"vue\\")
7474
const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
7575
7676
return function ssrRender(_ctx, _push, _parent) {
@@ -82,7 +82,7 @@ describe('ssr: components', () => {
8282
_push(\`\${_ssrInterpolate(msg + _ctx.outer)}\`)
8383
} else {
8484
return [
85-
_createTextVNode(_toDisplayString(msg + _ctx.outer))
85+
_createTextVNode(_toDisplayString(msg + _ctx.outer), 1 /* TEXT */)
8686
]
8787
}
8888
},
@@ -168,7 +168,7 @@ describe('ssr: components', () => {
168168
<template v-for="key in names" v-slot:[key]="{ msg }">{{ msg + key + bar }}</template>
169169
</foo>`).code
170170
).toMatchInlineSnapshot(`
171-
"const { resolveComponent: _resolveComponent, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\")
171+
"const { resolveComponent: _resolveComponent, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\")
172172
const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
173173
174174
return function ssrRender(_ctx, _push, _parent) {
@@ -183,7 +183,7 @@ describe('ssr: components', () => {
183183
_push(\`\${_ssrInterpolate(msg + key + _ctx.bar)}\`)
184184
} else {
185185
return [
186-
_createTextVNode(_toDisplayString(msg + _ctx.key + _ctx.bar))
186+
_createTextVNode(_toDisplayString(msg + _ctx.key + _ctx.bar), 1 /* TEXT */)
187187
]
188188
}
189189
}

packages/compiler-ssr/src/transforms/ssrTransformComponent.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
8080
const clonedNode = clone(node)
8181

8282
return function ssrPostTransformComponent() {
83+
// Using the cloned node, build the normal VNode-based branches (for
84+
// fallback in case the child is render-fn based). Store them in an array
85+
// for later use.
8386
buildSlots(clonedNode, context, (props, children) => {
8487
vnodeBranches.push(createVNodeSlotBranch(props, children, context))
8588
return createFunctionExpression(undefined)
@@ -106,9 +109,7 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
106109
wipEntries.push({
107110
fn,
108111
children,
109-
// build the children using normal vnode-based transforms
110-
// TODO fixme: `children` here has already been mutated at this point
111-
// so the sub-transform runs into errors :/
112+
// also collect the corresponding vnode branch built earlier
112113
vnodeBranch: vnodeBranches[wipEntries.length]
113114
})
114115
return fn
@@ -266,6 +267,9 @@ function subTransform(
266267
) {
267268
const childRoot = createRoot([node])
268269
const childContext = createTransformContext(childRoot, options)
270+
// this sub transform is for vnode fallback branch so it should be handled
271+
// like normal render functions
272+
childContext.ssr = false
269273
// inherit parent scope analysis state
270274
childContext.scopes = { ...parentContext.scopes }
271275
childContext.identifiers = { ...parentContext.identifiers }

packages/compiler-ssr/src/transforms/ssrVSlot.ts

-1
This file was deleted.

packages/runtime-core/src/component.ts

+19-10
Original file line numberDiff line numberDiff line change
@@ -300,15 +300,16 @@ export function setupComponent(
300300
// setup stateful logic
301301
let setupResult
302302
if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
303-
setupResult = setupStatefulComponent(instance, parentSuspense)
303+
setupResult = setupStatefulComponent(instance, parentSuspense, isSSR)
304304
}
305305
isInSSRComponentSetup = false
306306
return setupResult
307307
}
308308

309309
function setupStatefulComponent(
310310
instance: ComponentInternalInstance,
311-
parentSuspense: SuspenseBoundary | null
311+
parentSuspense: SuspenseBoundary | null,
312+
isSSR: boolean
312313
) {
313314
const Component = instance.type as ComponentOptions
314315

@@ -362,7 +363,7 @@ function setupStatefulComponent(
362363
if (isInSSRComponentSetup) {
363364
// return the promise so server-renderer can wait on it
364365
return setupResult.then(resolvedResult => {
365-
handleSetupResult(instance, resolvedResult, parentSuspense)
366+
handleSetupResult(instance, resolvedResult, parentSuspense, isSSR)
366367
})
367368
} else if (__FEATURE_SUSPENSE__) {
368369
// async setup returned Promise.
@@ -375,17 +376,18 @@ function setupStatefulComponent(
375376
)
376377
}
377378
} else {
378-
handleSetupResult(instance, setupResult, parentSuspense)
379+
handleSetupResult(instance, setupResult, parentSuspense, isSSR)
379380
}
380381
} else {
381-
finishComponentSetup(instance, parentSuspense)
382+
finishComponentSetup(instance, parentSuspense, isSSR)
382383
}
383384
}
384385

385386
export function handleSetupResult(
386387
instance: ComponentInternalInstance,
387388
setupResult: unknown,
388-
parentSuspense: SuspenseBoundary | null
389+
parentSuspense: SuspenseBoundary | null,
390+
isSSR: boolean
389391
) {
390392
if (isFunction(setupResult)) {
391393
// setup returned an inline render function
@@ -407,7 +409,7 @@ export function handleSetupResult(
407409
}`
408410
)
409411
}
410-
finishComponentSetup(instance, parentSuspense)
412+
finishComponentSetup(instance, parentSuspense, isSSR)
411413
}
412414

413415
type CompileFunction = (
@@ -424,10 +426,17 @@ export function registerRuntimeCompiler(_compile: any) {
424426

425427
function finishComponentSetup(
426428
instance: ComponentInternalInstance,
427-
parentSuspense: SuspenseBoundary | null
429+
parentSuspense: SuspenseBoundary | null,
430+
isSSR: boolean
428431
) {
429432
const Component = instance.type as ComponentOptions
430-
if (!instance.render) {
433+
434+
// template / render function normalization
435+
if (__NODE_JS__ && isSSR) {
436+
if (Component.render) {
437+
instance.render = Component.render as RenderFunction
438+
}
439+
} else if (!instance.render) {
431440
if (__RUNTIME_COMPILE__ && Component.template && !Component.render) {
432441
// __RUNTIME_COMPILE__ ensures `compile` is provided
433442
Component.render = compile!(Component.template, {
@@ -437,7 +446,7 @@ function finishComponentSetup(
437446
;(Component.render as RenderFunction).isRuntimeCompiled = true
438447
}
439448

440-
if (__DEV__ && !Component.render && !Component.ssrRender) {
449+
if (__DEV__ && !Component.render) {
441450
/* istanbul ignore if */
442451
if (!__RUNTIME_COMPILE__ && Component.template) {
443452
warn(

packages/runtime-core/src/components/Suspense.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ function createSuspenseBoundary<HostNode, HostElement>(
410410
if (__DEV__) {
411411
pushWarningContext(vnode)
412412
}
413-
handleSetupResult(instance, asyncSetupResult, suspense)
413+
handleSetupResult(instance, asyncSetupResult, suspense, false)
414414
// unset placeholder, otherwise this will be treated as a hydration mount
415415
vnode.el = null
416416
setupRenderEffect(

packages/server-renderer/src/renderToString.ts

+32-32
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,37 @@ function renderComponentVNode(
155155
}
156156
}
157157

158+
function renderComponentSubTree(
159+
instance: ComponentInternalInstance
160+
): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
161+
const comp = instance.type as Component
162+
const { getBuffer, push } = createBuffer()
163+
if (isFunction(comp)) {
164+
renderVNode(push, renderComponentRoot(instance), instance)
165+
} else {
166+
if (!instance.render && !comp.ssrRender && isString(comp.template)) {
167+
comp.ssrRender = ssrCompile(comp.template, instance)
168+
}
169+
170+
if (comp.ssrRender) {
171+
// optimized
172+
// set current rendering instance for asset resolution
173+
setCurrentRenderingInstance(instance)
174+
comp.ssrRender(instance.proxy, push, instance)
175+
setCurrentRenderingInstance(null)
176+
} else if (instance.render) {
177+
renderVNode(push, renderComponentRoot(instance), instance)
178+
} else {
179+
throw new Error(
180+
`Component ${
181+
comp.name ? `${comp.name} ` : ``
182+
} is missing template or render function.`
183+
)
184+
}
185+
}
186+
return getBuffer()
187+
}
188+
158189
type SSRRenderFunction = (
159190
context: any,
160191
push: (item: any) => void,
@@ -190,38 +221,7 @@ function ssrCompile(
190221
}
191222
}
192223
})
193-
return (compileCache[template] = Function(code)())
194-
}
195-
196-
function renderComponentSubTree(
197-
instance: ComponentInternalInstance
198-
): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
199-
const comp = instance.type as Component
200-
const { getBuffer, push } = createBuffer()
201-
if (isFunction(comp)) {
202-
renderVNode(push, renderComponentRoot(instance), instance)
203-
} else {
204-
if (!instance.render && !comp.ssrRender && isString(comp.template)) {
205-
comp.ssrRender = ssrCompile(comp.template, instance)
206-
}
207-
208-
if (comp.ssrRender) {
209-
// optimized
210-
// set current rendering instance for asset resolution
211-
setCurrentRenderingInstance(instance)
212-
comp.ssrRender(instance.proxy, push, instance)
213-
setCurrentRenderingInstance(null)
214-
} else if (instance.render) {
215-
renderVNode(push, renderComponentRoot(instance), instance)
216-
} else {
217-
throw new Error(
218-
`Component ${
219-
comp.name ? `${comp.name} ` : ``
220-
} is missing template or render function.`
221-
)
222-
}
223-
}
224-
return getBuffer()
224+
return (compileCache[template] = Function('require', code)(require))
225225
}
226226

227227
function renderVNode(

0 commit comments

Comments
 (0)