fix(ssr): render fallthrough attributes for transition-group with tag

fix #5141
This commit is contained in:
Evan You 2022-05-18 09:55:39 +08:00
parent 1035c6b51b
commit aed10c5072
3 changed files with 98 additions and 10 deletions

View File

@ -26,10 +26,10 @@ describe('transition-group', () => {
`<transition-group tag="ul"><div v-for="i in list"/></transition-group>`
).code
).toMatchInlineSnapshot(`
"const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
"const { ssrRenderAttrs: _ssrRenderAttrs, ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<ul>\`)
_push(\`<ul\${_ssrRenderAttrs(_attrs)}>\`)
_ssrRenderList(_ctx.list, (i) => {
_push(\`<div></div>\`)
})
@ -44,10 +44,14 @@ describe('transition-group', () => {
`<transition-group :tag="someTag"><div v-for="i in list"/></transition-group>`
).code
).toMatchInlineSnapshot(`
"const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
"const { ssrRenderAttrs: _ssrRenderAttrs, ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<\${_ctx.someTag}>\`)
_push(\`<\${
_ctx.someTag
}\${
_ssrRenderAttrs(_attrs)
}>\`)
_ssrRenderList(_ctx.list, (i) => {
_push(\`<div></div>\`)
})
@ -85,4 +89,23 @@ describe('transition-group', () => {
}"
`)
})
test('attribute fallthrough', () => {
expect(
compile(
`<transition-group tag="ul" class="red" id="ok">
</transition-group>`
).code
).toMatchInlineSnapshot(`
"const { mergeProps: _mergeProps } = require(\\"vue\\")
const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<ul\${_ssrRenderAttrs(_mergeProps({
class: \\"red\\",
id: \\"ok\\"
}, _attrs))}></ul>\`)
}"
`)
})
})

View File

@ -48,7 +48,10 @@ import {
ssrProcessSuspense,
ssrTransformSuspense
} from './ssrTransformSuspense'
import { ssrProcessTransitionGroup } from './ssrTransformTransitionGroup'
import {
ssrProcessTransitionGroup,
ssrTransformTransitionGroup
} from './ssrTransformTransitionGroup'
import { isSymbol, isObject, isArray } from '@vue/shared'
import { buildSSRProps } from './ssrTransformElement'
@ -95,7 +98,10 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
if (component === SUSPENSE) {
return ssrTransformSuspense(node, context)
}
return // built-in component: fallthrough
if (component === TRANSITION_GROUP) {
return ssrTransformTransitionGroup(node, context)
}
return // other built-in components: fallthrough
}
// Build the fallback vnode-based branch for the component's slots.

View File

@ -1,16 +1,71 @@
import { ComponentNode, findProp, NodeTypes } from '@vue/compiler-dom'
import {
AttributeNode,
buildProps,
ComponentNode,
createCallExpression,
DirectiveNode,
findProp,
JSChildNode,
NodeTypes,
TransformContext
} from '@vue/compiler-dom'
import { SSR_RENDER_ATTRS } from '../runtimeHelpers'
import { processChildren, SSRTransformContext } from '../ssrCodegenTransform'
import { buildSSRProps } from './ssrTransformElement'
const wipMap = new WeakMap<ComponentNode, WIPEntry>()
interface WIPEntry {
tag: AttributeNode | DirectiveNode
propsExp: string | JSChildNode | null
}
// phase 1: build props
export function ssrTransformTransitionGroup(
node: ComponentNode,
context: TransformContext
) {
return () => {
const tag = findProp(node, 'tag')
if (tag) {
const otherProps = node.props.filter(p => p !== tag)
const { props, directives } = buildProps(
node,
context,
otherProps,
true, /* isComponent */
false, /* isDynamicComponent */
true /* ssr (skip event listeners) */
)
let propsExp = null
if (props || directives.length) {
propsExp = createCallExpression(context.helper(SSR_RENDER_ATTRS), [
buildSSRProps(props, directives, context)
])
}
wipMap.set(node, {
tag,
propsExp
})
}
}
}
// phase 2: process children
export function ssrProcessTransitionGroup(
node: ComponentNode,
context: SSRTransformContext
) {
const tag = findProp(node, 'tag')
if (tag) {
const entry = wipMap.get(node)
if (entry) {
const { tag, propsExp } = entry
if (tag.type === NodeTypes.DIRECTIVE) {
// dynamic :tag
context.pushStringPart(`<`)
context.pushStringPart(tag.exp!)
if (propsExp) {
context.pushStringPart(propsExp)
}
context.pushStringPart(`>`)
processChildren(
@ -30,7 +85,11 @@ export function ssrProcessTransitionGroup(
context.pushStringPart(`>`)
} else {
// static tag
context.pushStringPart(`<${tag.value!.content}>`)
context.pushStringPart(`<${tag.value!.content}`)
if (propsExp) {
context.pushStringPart(propsExp)
}
context.pushStringPart(`>`)
processChildren(node, context, false, true)
context.pushStringPart(`</${tag.value!.content}>`)
}