fix(ssr): fix ssr on-the-fly compilation + slot fallback branch helper injection
This commit is contained in:
parent
274f81c5db
commit
3be3785f94
@ -70,7 +70,7 @@ describe('ssr: components', () => {
|
|||||||
test('explicit default slot', () => {
|
test('explicit default slot', () => {
|
||||||
expect(compile(`<foo v-slot="{ msg }">{{ msg + outer }}</foo>`).code)
|
expect(compile(`<foo v-slot="{ msg }">{{ msg + outer }}</foo>`).code)
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"const { resolveComponent: _resolveComponent, createTextVNode: _createTextVNode } = require(\\"vue\\")
|
"const { resolveComponent: _resolveComponent, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode } = require(\\"vue\\")
|
||||||
const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
|
const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
|
||||||
|
|
||||||
return function ssrRender(_ctx, _push, _parent) {
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
@ -82,7 +82,7 @@ describe('ssr: components', () => {
|
|||||||
_push(\`\${_ssrInterpolate(msg + _ctx.outer)}\`)
|
_push(\`\${_ssrInterpolate(msg + _ctx.outer)}\`)
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
_createTextVNode(_toDisplayString(msg + _ctx.outer))
|
_createTextVNode(_toDisplayString(msg + _ctx.outer), 1 /* TEXT */)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -168,7 +168,7 @@ describe('ssr: components', () => {
|
|||||||
<template v-for="key in names" v-slot:[key]="{ msg }">{{ msg + key + bar }}</template>
|
<template v-for="key in names" v-slot:[key]="{ msg }">{{ msg + key + bar }}</template>
|
||||||
</foo>`).code
|
</foo>`).code
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"const { resolveComponent: _resolveComponent, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\")
|
"const { resolveComponent: _resolveComponent, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\")
|
||||||
const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
|
const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
|
||||||
|
|
||||||
return function ssrRender(_ctx, _push, _parent) {
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
@ -183,7 +183,7 @@ describe('ssr: components', () => {
|
|||||||
_push(\`\${_ssrInterpolate(msg + key + _ctx.bar)}\`)
|
_push(\`\${_ssrInterpolate(msg + key + _ctx.bar)}\`)
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
_createTextVNode(_toDisplayString(msg + _ctx.key + _ctx.bar))
|
_createTextVNode(_toDisplayString(msg + _ctx.key + _ctx.bar), 1 /* TEXT */)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,9 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
|
|||||||
const clonedNode = clone(node)
|
const clonedNode = clone(node)
|
||||||
|
|
||||||
return function ssrPostTransformComponent() {
|
return function ssrPostTransformComponent() {
|
||||||
|
// Using the cloned node, build the normal VNode-based branches (for
|
||||||
|
// fallback in case the child is render-fn based). Store them in an array
|
||||||
|
// for later use.
|
||||||
buildSlots(clonedNode, context, (props, children) => {
|
buildSlots(clonedNode, context, (props, children) => {
|
||||||
vnodeBranches.push(createVNodeSlotBranch(props, children, context))
|
vnodeBranches.push(createVNodeSlotBranch(props, children, context))
|
||||||
return createFunctionExpression(undefined)
|
return createFunctionExpression(undefined)
|
||||||
@ -106,9 +109,7 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
|
|||||||
wipEntries.push({
|
wipEntries.push({
|
||||||
fn,
|
fn,
|
||||||
children,
|
children,
|
||||||
// build the children using normal vnode-based transforms
|
// also collect the corresponding vnode branch built earlier
|
||||||
// TODO fixme: `children` here has already been mutated at this point
|
|
||||||
// so the sub-transform runs into errors :/
|
|
||||||
vnodeBranch: vnodeBranches[wipEntries.length]
|
vnodeBranch: vnodeBranches[wipEntries.length]
|
||||||
})
|
})
|
||||||
return fn
|
return fn
|
||||||
@ -266,6 +267,9 @@ function subTransform(
|
|||||||
) {
|
) {
|
||||||
const childRoot = createRoot([node])
|
const childRoot = createRoot([node])
|
||||||
const childContext = createTransformContext(childRoot, options)
|
const childContext = createTransformContext(childRoot, options)
|
||||||
|
// this sub transform is for vnode fallback branch so it should be handled
|
||||||
|
// like normal render functions
|
||||||
|
childContext.ssr = false
|
||||||
// inherit parent scope analysis state
|
// inherit parent scope analysis state
|
||||||
childContext.scopes = { ...parentContext.scopes }
|
childContext.scopes = { ...parentContext.scopes }
|
||||||
childContext.identifiers = { ...parentContext.identifiers }
|
childContext.identifiers = { ...parentContext.identifiers }
|
||||||
|
@ -1 +0,0 @@
|
|||||||
// TODO
|
|
@ -300,7 +300,7 @@ export function setupComponent(
|
|||||||
// setup stateful logic
|
// setup stateful logic
|
||||||
let setupResult
|
let setupResult
|
||||||
if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
|
if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
|
||||||
setupResult = setupStatefulComponent(instance, parentSuspense)
|
setupResult = setupStatefulComponent(instance, parentSuspense, isSSR)
|
||||||
}
|
}
|
||||||
isInSSRComponentSetup = false
|
isInSSRComponentSetup = false
|
||||||
return setupResult
|
return setupResult
|
||||||
@ -308,7 +308,8 @@ export function setupComponent(
|
|||||||
|
|
||||||
function setupStatefulComponent(
|
function setupStatefulComponent(
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
parentSuspense: SuspenseBoundary | null
|
parentSuspense: SuspenseBoundary | null,
|
||||||
|
isSSR: boolean
|
||||||
) {
|
) {
|
||||||
const Component = instance.type as ComponentOptions
|
const Component = instance.type as ComponentOptions
|
||||||
|
|
||||||
@ -362,7 +363,7 @@ function setupStatefulComponent(
|
|||||||
if (isInSSRComponentSetup) {
|
if (isInSSRComponentSetup) {
|
||||||
// return the promise so server-renderer can wait on it
|
// return the promise so server-renderer can wait on it
|
||||||
return setupResult.then(resolvedResult => {
|
return setupResult.then(resolvedResult => {
|
||||||
handleSetupResult(instance, resolvedResult, parentSuspense)
|
handleSetupResult(instance, resolvedResult, parentSuspense, isSSR)
|
||||||
})
|
})
|
||||||
} else if (__FEATURE_SUSPENSE__) {
|
} else if (__FEATURE_SUSPENSE__) {
|
||||||
// async setup returned Promise.
|
// async setup returned Promise.
|
||||||
@ -375,17 +376,18 @@ function setupStatefulComponent(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
handleSetupResult(instance, setupResult, parentSuspense)
|
handleSetupResult(instance, setupResult, parentSuspense, isSSR)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
finishComponentSetup(instance, parentSuspense)
|
finishComponentSetup(instance, parentSuspense, isSSR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleSetupResult(
|
export function handleSetupResult(
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
setupResult: unknown,
|
setupResult: unknown,
|
||||||
parentSuspense: SuspenseBoundary | null
|
parentSuspense: SuspenseBoundary | null,
|
||||||
|
isSSR: boolean
|
||||||
) {
|
) {
|
||||||
if (isFunction(setupResult)) {
|
if (isFunction(setupResult)) {
|
||||||
// setup returned an inline render function
|
// setup returned an inline render function
|
||||||
@ -407,7 +409,7 @@ export function handleSetupResult(
|
|||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
finishComponentSetup(instance, parentSuspense)
|
finishComponentSetup(instance, parentSuspense, isSSR)
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompileFunction = (
|
type CompileFunction = (
|
||||||
@ -424,10 +426,17 @@ export function registerRuntimeCompiler(_compile: any) {
|
|||||||
|
|
||||||
function finishComponentSetup(
|
function finishComponentSetup(
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
parentSuspense: SuspenseBoundary | null
|
parentSuspense: SuspenseBoundary | null,
|
||||||
|
isSSR: boolean
|
||||||
) {
|
) {
|
||||||
const Component = instance.type as ComponentOptions
|
const Component = instance.type as ComponentOptions
|
||||||
if (!instance.render) {
|
|
||||||
|
// template / render function normalization
|
||||||
|
if (__NODE_JS__ && isSSR) {
|
||||||
|
if (Component.render) {
|
||||||
|
instance.render = Component.render as RenderFunction
|
||||||
|
}
|
||||||
|
} else if (!instance.render) {
|
||||||
if (__RUNTIME_COMPILE__ && Component.template && !Component.render) {
|
if (__RUNTIME_COMPILE__ && Component.template && !Component.render) {
|
||||||
// __RUNTIME_COMPILE__ ensures `compile` is provided
|
// __RUNTIME_COMPILE__ ensures `compile` is provided
|
||||||
Component.render = compile!(Component.template, {
|
Component.render = compile!(Component.template, {
|
||||||
@ -437,7 +446,7 @@ function finishComponentSetup(
|
|||||||
;(Component.render as RenderFunction).isRuntimeCompiled = true
|
;(Component.render as RenderFunction).isRuntimeCompiled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__DEV__ && !Component.render && !Component.ssrRender) {
|
if (__DEV__ && !Component.render) {
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
if (!__RUNTIME_COMPILE__ && Component.template) {
|
if (!__RUNTIME_COMPILE__ && Component.template) {
|
||||||
warn(
|
warn(
|
||||||
|
@ -410,7 +410,7 @@ function createSuspenseBoundary<HostNode, HostElement>(
|
|||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
pushWarningContext(vnode)
|
pushWarningContext(vnode)
|
||||||
}
|
}
|
||||||
handleSetupResult(instance, asyncSetupResult, suspense)
|
handleSetupResult(instance, asyncSetupResult, suspense, false)
|
||||||
// unset placeholder, otherwise this will be treated as a hydration mount
|
// unset placeholder, otherwise this will be treated as a hydration mount
|
||||||
vnode.el = null
|
vnode.el = null
|
||||||
setupRenderEffect(
|
setupRenderEffect(
|
||||||
|
@ -155,6 +155,37 @@ function renderComponentVNode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderComponentSubTree(
|
||||||
|
instance: ComponentInternalInstance
|
||||||
|
): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
|
||||||
|
const comp = instance.type as Component
|
||||||
|
const { getBuffer, push } = createBuffer()
|
||||||
|
if (isFunction(comp)) {
|
||||||
|
renderVNode(push, renderComponentRoot(instance), instance)
|
||||||
|
} else {
|
||||||
|
if (!instance.render && !comp.ssrRender && isString(comp.template)) {
|
||||||
|
comp.ssrRender = ssrCompile(comp.template, instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comp.ssrRender) {
|
||||||
|
// optimized
|
||||||
|
// set current rendering instance for asset resolution
|
||||||
|
setCurrentRenderingInstance(instance)
|
||||||
|
comp.ssrRender(instance.proxy, push, instance)
|
||||||
|
setCurrentRenderingInstance(null)
|
||||||
|
} else if (instance.render) {
|
||||||
|
renderVNode(push, renderComponentRoot(instance), instance)
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Component ${
|
||||||
|
comp.name ? `${comp.name} ` : ``
|
||||||
|
} is missing template or render function.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getBuffer()
|
||||||
|
}
|
||||||
|
|
||||||
type SSRRenderFunction = (
|
type SSRRenderFunction = (
|
||||||
context: any,
|
context: any,
|
||||||
push: (item: any) => void,
|
push: (item: any) => void,
|
||||||
@ -190,38 +221,7 @@ function ssrCompile(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return (compileCache[template] = Function(code)())
|
return (compileCache[template] = Function('require', code)(require))
|
||||||
}
|
|
||||||
|
|
||||||
function renderComponentSubTree(
|
|
||||||
instance: ComponentInternalInstance
|
|
||||||
): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
|
|
||||||
const comp = instance.type as Component
|
|
||||||
const { getBuffer, push } = createBuffer()
|
|
||||||
if (isFunction(comp)) {
|
|
||||||
renderVNode(push, renderComponentRoot(instance), instance)
|
|
||||||
} else {
|
|
||||||
if (!instance.render && !comp.ssrRender && isString(comp.template)) {
|
|
||||||
comp.ssrRender = ssrCompile(comp.template, instance)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (comp.ssrRender) {
|
|
||||||
// optimized
|
|
||||||
// set current rendering instance for asset resolution
|
|
||||||
setCurrentRenderingInstance(instance)
|
|
||||||
comp.ssrRender(instance.proxy, push, instance)
|
|
||||||
setCurrentRenderingInstance(null)
|
|
||||||
} else if (instance.render) {
|
|
||||||
renderVNode(push, renderComponentRoot(instance), instance)
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
`Component ${
|
|
||||||
comp.name ? `${comp.name} ` : ``
|
|
||||||
} is missing template or render function.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return getBuffer()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderVNode(
|
function renderVNode(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user