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

fix(compiler-ssr): fix wrong attrs fallthrough on non-single-root v-if branches

fix #5140
Evan You 3 лет назад
Родитель
Сommit
516bc548fc

+ 1 - 1
packages/compiler-core/src/transform.ts

@@ -68,7 +68,7 @@ export interface DirectiveTransformResult {
   ssrTagParts?: TemplateLiteral['elements']
 }
 
-// A structural directive transform is a technically a NodeTransform;
+// A structural directive transform is technically also a NodeTransform;
 // Only v-if and v-for fall into this category.
 export type StructuralDirectiveTransform = (
   node: ElementNode,

+ 0 - 21
packages/compiler-ssr/__tests__/ssrComponent.spec.ts

@@ -306,27 +306,6 @@ describe('ssr: components', () => {
               `)
       })
 
-      test('should inject attrs if root with coomments', () => {
-        expect(compile(`<!--root--><transition><div/></transition>`).code)
-          .toMatchInlineSnapshot(`
-                  "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
-
-                  return function ssrRender(_ctx, _push, _parent, _attrs) {
-                    _push(\`<!--[--><!--root--><div\${_ssrRenderAttrs(_attrs)}></div><!--]-->\`)
-                  }"
-              `)
-      })
-
-      test('should not inject attrs if not root', () => {
-        expect(compile(`<div/><transition><div/></transition>`).code)
-          .toMatchInlineSnapshot(`
-                  "
-                  return function ssrRender(_ctx, _push, _parent, _attrs) {
-                    _push(\`<!--[--><div></div><div></div><!--]-->\`)
-                  }"
-              `)
-      })
-
       // #5352
       test('should push marker string if is slot root', () => {
         expect(

+ 60 - 0
packages/compiler-ssr/__tests__/ssrFallthroughAttrs.spec.ts

@@ -0,0 +1,60 @@
+import { compile } from '../src'
+
+describe('ssr: attrs fallthrough', () => {
+  test('basic', () => {
+    expect(compile(`<div/>`).code).toMatchInlineSnapshot(`
+      "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
+
+      return function ssrRender(_ctx, _push, _parent, _attrs) {
+        _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
+      }"
+    `)
+  })
+
+  test('with comments', () => {
+    expect(compile(`<!--!--><div/>`).code).toMatchInlineSnapshot(`
+      "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
+
+      return function ssrRender(_ctx, _push, _parent, _attrs) {
+        _push(\`<!--[--><!--!--><div\${_ssrRenderAttrs(_attrs)}></div><!--]-->\`)
+      }"
+    `)
+  })
+
+  // #5140
+  test('should not inject to non-single-root if branches', () => {
+    expect(compile(`<div v-if="true"/><div/>`).code).toMatchInlineSnapshot(`
+      "
+      return function ssrRender(_ctx, _push, _parent, _attrs) {
+        _push(\`<!--[-->\`)
+        if (true) {
+          _push(\`<div></div>\`)
+        } else {
+          _push(\`<!---->\`)
+        }
+        _push(\`<div></div><!--]-->\`)
+      }"
+    `)
+  })
+
+  test('fallthrough component content (root with coomments)', () => {
+    expect(compile(`<!--root--><transition><div/></transition>`).code)
+      .toMatchInlineSnapshot(`
+              "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
+
+              return function ssrRender(_ctx, _push, _parent, _attrs) {
+                _push(\`<!--[--><!--root--><div\${_ssrRenderAttrs(_attrs)}></div><!--]-->\`)
+              }"
+          `)
+  })
+
+  test('should not inject to fallthrough component content if not root', () => {
+    expect(compile(`<div/><transition><div/></transition>`).code)
+      .toMatchInlineSnapshot(`
+              "
+              return function ssrRender(_ctx, _push, _parent, _attrs) {
+                _push(\`<!--[--><div></div><div></div><!--]-->\`)
+              }"
+          `)
+  })
+})

+ 19 - 0
packages/compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts

@@ -46,6 +46,25 @@ export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => {
   }
 
   if (node.type === NodeTypes.IF_BRANCH && hasSingleChild(node)) {
+    // detect cases where the parent v-if is not the only root level node
+    let hasEncounteredIf = false
+    for (const c of filterChild(parent)) {
+      if (
+        c.type === NodeTypes.IF ||
+        (c.type === NodeTypes.ELEMENT && findDir(c, 'if'))
+      ) {
+        // multiple root v-if
+        if (hasEncounteredIf) return
+        hasEncounteredIf = true
+      } else if (
+        // node before v-if
+        !hasEncounteredIf ||
+        // non else nodes
+        !(c.type === NodeTypes.ELEMENT && findDir(c, /else/, true))
+      ) {
+        return
+      }
+    }
     injectFallthroughAttrs(node.children[0])
   } else if (hasSingleChild(parent)) {
     injectFallthroughAttrs(node)