From ee22c6d9a72f90936443d2f58aeb2dfb69f65935 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 11 Mar 2018 17:09:51 -0400 Subject: [PATCH] fix(ssr): fix SSR for async functional components fix #7784 --- src/server/render-context.js | 7 ++++- src/server/render.js | 22 +++++++++++++-- test/ssr/ssr-string.spec.js | 54 ++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src/server/render-context.js b/src/server/render-context.js index cecb00157d7..b808f6a838d 100644 --- a/src/server/render-context.js +++ b/src/server/render-context.js @@ -67,13 +67,18 @@ export class RenderContext { } switch (lastState.type) { case 'Element': + case 'Fragment': const { children, total } = lastState const rendered = lastState.rendered++ if (rendered < total) { this.renderNode(children[rendered], false, this) } else { this.renderStates.pop() - this.write(lastState.endTag, this.next) + if (lastState.endTag) { + this.write(lastState.endTag, this.next) + } else { + this.next() + } } break case 'Component': diff --git a/src/server/render.js b/src/server/render.js index 150afce077d..c570cc78176 100644 --- a/src/server/render.js +++ b/src/server/render.js @@ -191,7 +191,21 @@ function renderAsyncComponent (node, isRoot, context) { tag ) if (resolvedNode) { - renderComponent(resolvedNode, isRoot, context) + if (resolvedNode.componnetInstance) { + renderComponent(resolvedNode, isRoot, context) + } else if (!Array.isArray(resolvedNode)) { + // single return node from functional component + renderNode(resolvedNode, isRoot, context) + } else { + // multiple return nodes from functional component + context.renderStates.push({ + type: 'Fragment', + children: resolvedNode, + rendered: 0, + total: resolvedNode.length + }) + context.next() + } } else { // invalid component, but this does not throw on the client // so render empty comment node @@ -232,9 +246,10 @@ function renderStringNode (el, context) { const children: Array = el.children context.renderStates.push({ type: 'Element', + children, rendered: 0, total: children.length, - endTag: el.close, children + endTag: el.close }) write(el.open, next) } @@ -263,9 +278,10 @@ function renderElement (el, isRoot, context) { const children: Array = el.children context.renderStates.push({ type: 'Element', + children, rendered: 0, total: children.length, - endTag, children + endTag }) write(startTag, next) } diff --git a/test/ssr/ssr-string.spec.js b/test/ssr/ssr-string.spec.js index f370618d8bc..4d3ace46019 100644 --- a/test/ssr/ssr-string.spec.js +++ b/test/ssr/ssr-string.spec.js @@ -575,6 +575,60 @@ describe('SSR: renderToString', () => { }) }) + it('renders async component (functional, single node)', done => { + renderVmWithOptions({ + template: ` +
+ +
+ `, + components: { + testAsync (resolve) { + setTimeout(() => resolve({ + functional: true, + render (h) { + return h('span', { class: ['b'] }, 'testAsync') + } + }), 1) + } + } + }, result => { + expect(result).toContain('
testAsync
') + done() + }) + }) + + it('renders async component (functional, multiple nodes)', done => { + renderVmWithOptions({ + template: ` +
+ +
+ `, + components: { + testAsync (resolve) { + setTimeout(() => resolve({ + functional: true, + render (h) { + return [ + h('span', { class: ['a'] }, 'foo'), + h('span', { class: ['b'] }, 'bar') + ] + } + }), 1) + } + } + }, result => { + expect(result).toContain( + '
' + + 'foo' + + 'bar' + + '
' + ) + done() + }) + }) + it('should catch async component error', done => { Vue.config.silent = true renderToString(new Vue({