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>` `<transition-group tag="ul"><div v-for="i in list"/></transition-group>`
).code ).code
).toMatchInlineSnapshot(` ).toMatchInlineSnapshot(`
"const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\") "const { ssrRenderAttrs: _ssrRenderAttrs, ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent, _attrs) { return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<ul>\`) _push(\`<ul\${_ssrRenderAttrs(_attrs)}>\`)
_ssrRenderList(_ctx.list, (i) => { _ssrRenderList(_ctx.list, (i) => {
_push(\`<div></div>\`) _push(\`<div></div>\`)
}) })
@ -44,10 +44,14 @@ describe('transition-group', () => {
`<transition-group :tag="someTag"><div v-for="i in list"/></transition-group>` `<transition-group :tag="someTag"><div v-for="i in list"/></transition-group>`
).code ).code
).toMatchInlineSnapshot(` ).toMatchInlineSnapshot(`
"const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\") "const { ssrRenderAttrs: _ssrRenderAttrs, ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent, _attrs) { return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<\${_ctx.someTag}>\`) _push(\`<\${
_ctx.someTag
}\${
_ssrRenderAttrs(_attrs)
}>\`)
_ssrRenderList(_ctx.list, (i) => { _ssrRenderList(_ctx.list, (i) => {
_push(\`<div></div>\`) _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, ssrProcessSuspense,
ssrTransformSuspense ssrTransformSuspense
} from './ssrTransformSuspense' } from './ssrTransformSuspense'
import { ssrProcessTransitionGroup } from './ssrTransformTransitionGroup' import {
ssrProcessTransitionGroup,
ssrTransformTransitionGroup
} from './ssrTransformTransitionGroup'
import { isSymbol, isObject, isArray } from '@vue/shared' import { isSymbol, isObject, isArray } from '@vue/shared'
import { buildSSRProps } from './ssrTransformElement' import { buildSSRProps } from './ssrTransformElement'
@ -95,7 +98,10 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
if (component === SUSPENSE) { if (component === SUSPENSE) {
return ssrTransformSuspense(node, context) 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. // 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 { 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( export function ssrProcessTransitionGroup(
node: ComponentNode, node: ComponentNode,
context: SSRTransformContext context: SSRTransformContext
) { ) {
const tag = findProp(node, 'tag') const entry = wipMap.get(node)
if (tag) { if (entry) {
const { tag, propsExp } = entry
if (tag.type === NodeTypes.DIRECTIVE) { if (tag.type === NodeTypes.DIRECTIVE) {
// dynamic :tag // dynamic :tag
context.pushStringPart(`<`) context.pushStringPart(`<`)
context.pushStringPart(tag.exp!) context.pushStringPart(tag.exp!)
if (propsExp) {
context.pushStringPart(propsExp)
}
context.pushStringPart(`>`) context.pushStringPart(`>`)
processChildren( processChildren(
@ -30,7 +85,11 @@ export function ssrProcessTransitionGroup(
context.pushStringPart(`>`) context.pushStringPart(`>`)
} else { } else {
// static tag // static tag
context.pushStringPart(`<${tag.value!.content}>`) context.pushStringPart(`<${tag.value!.content}`)
if (propsExp) {
context.pushStringPart(propsExp)
}
context.pushStringPart(`>`)
processChildren(node, context, false, true) processChildren(node, context, false, true)
context.pushStringPart(`</${tag.value!.content}>`) context.pushStringPart(`</${tag.value!.content}>`)
} }