Просмотр исходного кода

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

fix #5141
Evan You 4 лет назад
Родитель
Сommit
aed10c5072

+ 27 - 4
packages/compiler-ssr/__tests__/ssrTransitionGroup.spec.ts

@@ -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>\`)
+      }"
+    `)
+  })
 })

+ 8 - 2
packages/compiler-ssr/src/transforms/ssrTransformComponent.ts

@@ -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.

+ 63 - 4
packages/compiler-ssr/src/transforms/ssrTransformTransitionGroup.ts

@@ -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}>`)
     }