wip(compiler-ssr): built-in component fallthrough

This commit is contained in:
Evan You
2020-02-06 15:29:02 -05:00
parent 9cfbab0686
commit 3c27bf6133
10 changed files with 129 additions and 61 deletions

View File

@@ -161,5 +161,47 @@ describe('ssr: components', () => {
}"
`)
})
test('built-in fallthroughs', () => {
// no fragment
expect(compile(`<transition><div/></transition>`).code)
.toMatchInlineSnapshot(`
"
return function ssrRender(_ctx, _push, _parent) {
_push(\`<div></div>\`)
}"
`)
// wrap with fragment
expect(compile(`<transition-group><div/></transition-group>`).code)
.toMatchInlineSnapshot(`
"
return function ssrRender(_ctx, _push, _parent) {
_push(\`<!----><div></div><!---->\`)
}"
`)
// no fragment
expect(compile(`<keep-alive><foo/></keep-alive>`).code)
.toMatchInlineSnapshot(`
"const { resolveComponent } = require(\\"vue\\")
const { _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent) {
const _component_foo = resolveComponent(\\"foo\\")
_ssrRenderComponent(_component_foo, null, null, _parent)
}"
`)
// wrap with fragment
expect(compile(`<suspense><div/></suspense>`).code)
.toMatchInlineSnapshot(`
"
return function ssrRender(_ctx, _push, _parent) {
_push(\`<!----><div></div><!---->\`)
}"
`)
})
})
})

View File

@@ -10,7 +10,8 @@ import {
trackSlotScopes,
noopDirectiveTransform,
transformBind,
transformStyle
transformStyle,
isBuiltInDOMComponent
} from '@vue/compiler-dom'
import { ssrCodegenTransform } from './ssrCodegenTransform'
import { ssrTransformElement } from './transforms/ssrTransformElement'
@@ -64,7 +65,8 @@ export function compile(
cloak: noopDirectiveTransform,
once: noopDirectiveTransform,
...(options.directiveTransforms || {}) // user transforms
}
},
isBuiltInComponent: isBuiltInDOMComponent
})
// traverse the template AST and convert into SSR codegen AST

View File

@@ -28,17 +28,9 @@ import { ssrProcessComponent } from './transforms/ssrTransformComponent'
export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) {
const context = createSSRTransformContext(options)
const isFragment =
ast.children.length > 1 && !ast.children.every(c => isText(c))
if (isFragment) {
context.pushStringPart(`<!---->`)
}
processChildren(ast.children, context)
if (isFragment) {
context.pushStringPart(`<!---->`)
}
processChildren(ast.children, context, isFragment)
ast.codegenNode = createBlockStatement(context.body)
// Finalize helpers.
@@ -99,8 +91,12 @@ export function createChildContext(
export function processChildren(
children: TemplateChildNode[],
context: SSRTransformContext
context: SSRTransformContext,
asFragment = false
) {
if (asFragment) {
context.pushStringPart(`<!---->`)
}
const isVoidTag = context.options.isVoidTag || NO
for (let i = 0; i < children.length; i++) {
const child = children[i]
@@ -135,4 +131,7 @@ export function processChildren(
ssrProcessFor(child, context)
}
}
if (asFragment) {
context.pushStringPart(`<!---->`)
}
}

View File

@@ -6,14 +6,15 @@ import {
resolveComponentType,
buildProps,
ComponentNode,
PORTAL,
SUSPENSE,
SlotFnBuilder,
createFunctionExpression,
createBlockStatement,
buildSlots,
FunctionExpression,
TemplateChildNode
TemplateChildNode,
PORTAL,
SUSPENSE,
TRANSITION_GROUP
} from '@vue/compiler-dom'
import { SSR_RENDER_COMPONENT } from '../runtimeHelpers'
import {
@@ -34,6 +35,8 @@ interface WIPSlotEntry {
children: TemplateChildNode[]
}
const componentTypeMap = new WeakMap<ComponentNode, symbol>()
export const ssrTransformComponent: NodeTransform = (node, context) => {
if (
node.type !== NodeTypes.ELEMENT ||
@@ -43,18 +46,10 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
}
return function ssrPostTransformComponent() {
const component = resolveComponentType(node, context)
const component = resolveComponentType(node, context, true /* ssr */)
if (isSymbol(component)) {
// built-in compoonent
if (component === PORTAL) {
// TODO
} else if (component === SUSPENSE) {
// TODO fallthrough
// TODO option to use fallback content and resolve on client
} else {
// TODO fallthrough for KeepAlive & Transition
}
componentTypeMap.set(node, component)
return // built-in component: fallthrough
}
// note we are not passing ssr: true here because for components, v-on
@@ -98,13 +93,28 @@ export function ssrProcessComponent(
node: ComponentNode,
context: SSRTransformContext
) {
// finish up slot function expressions from the 1st pass.
const wipEntries = wipMap.get(node) || []
for (let i = 0; i < wipEntries.length; i++) {
const { fn, children } = wipEntries[i]
const childContext = createChildContext(context)
processChildren(children, childContext)
fn.body = createBlockStatement(childContext.body)
if (!node.ssrCodegenNode) {
// this is a built-in component that fell-through.
// just render its children.
const component = componentTypeMap.get(node)!
if (component === PORTAL) {
// TODO
return
}
const needFragmentWrapper =
component === SUSPENSE || component === TRANSITION_GROUP
processChildren(node.children, context, needFragmentWrapper)
} else {
// finish up slot function expressions from the 1st pass.
const wipEntries = wipMap.get(node) || []
for (let i = 0; i < wipEntries.length; i++) {
const { fn, children } = wipEntries[i]
const childContext = createChildContext(context)
processChildren(children, childContext)
fn.body = createBlockStatement(childContext.body)
}
context.pushStatement(node.ssrCodegenNode)
}
context.pushStatement(node.ssrCodegenNode!)
}

View File

@@ -27,13 +27,7 @@ export function ssrProcessFor(node: ForNode, context: SSRTransformContext) {
const childContext = createChildContext(context)
const needFragmentWrapper =
node.children.length !== 1 || node.children[0].type !== NodeTypes.ELEMENT
if (needFragmentWrapper) {
childContext.pushStringPart(`<!---->`)
}
processChildren(node.children, childContext)
if (needFragmentWrapper) {
childContext.pushStringPart(`<!---->`)
}
processChildren(node.children, childContext, needFragmentWrapper)
const renderLoop = createFunctionExpression(
createForLoopParams(node.parseResult)
)

View File

@@ -64,12 +64,6 @@ function processIfBranch(
// optimize away nested fragments when the only child is a ForNode
!(children.length === 1 && children[0].type === NodeTypes.FOR)
const childContext = createChildContext(context)
if (needFragmentWrapper) {
childContext.pushStringPart(`<!---->`)
}
processChildren(children, childContext)
if (needFragmentWrapper) {
childContext.pushStringPart(`<!---->`)
}
processChildren(children, childContext, needFragmentWrapper)
return createBlockStatement(childContext.body)
}