Skip to content

Commit 882e719

Browse files
committed
fix(ssr): fix SSR for async functional components
fix #7784
1 parent 666cdbd commit 882e719

File tree

3 files changed

+79
-4
lines changed

3 files changed

+79
-4
lines changed

src/server/render-context.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,18 @@ export class RenderContext {
6767
}
6868
switch (lastState.type) {
6969
case 'Element':
70+
case 'Fragment':
7071
const { children, total } = lastState
7172
const rendered = lastState.rendered++
7273
if (rendered < total) {
7374
this.renderNode(children[rendered], false, this)
7475
} else {
7576
this.renderStates.pop()
76-
this.write(lastState.endTag, this.next)
77+
if (lastState.endTag) {
78+
this.write(lastState.endTag, this.next)
79+
} else {
80+
this.next()
81+
}
7782
}
7883
break
7984
case 'Component':

src/server/render.js

+19-3
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,21 @@ function renderAsyncComponent (node, isRoot, context) {
191191
tag
192192
)
193193
if (resolvedNode) {
194-
renderComponent(resolvedNode, isRoot, context)
194+
if (resolvedNode.componnetInstance) {
195+
renderComponent(resolvedNode, isRoot, context)
196+
} else if (!Array.isArray(resolvedNode)) {
197+
// single return node from functional component
198+
renderNode(resolvedNode, isRoot, context)
199+
} else {
200+
// multiple return nodes from functional component
201+
context.renderStates.push({
202+
type: 'Fragment',
203+
children: resolvedNode,
204+
rendered: 0,
205+
total: resolvedNode.length
206+
})
207+
context.next()
208+
}
195209
} else {
196210
// invalid component, but this does not throw on the client
197211
// so render empty comment node
@@ -232,9 +246,10 @@ function renderStringNode (el, context) {
232246
const children: Array<VNode> = el.children
233247
context.renderStates.push({
234248
type: 'Element',
249+
children,
235250
rendered: 0,
236251
total: children.length,
237-
endTag: el.close, children
252+
endTag: el.close
238253
})
239254
write(el.open, next)
240255
}
@@ -263,9 +278,10 @@ function renderElement (el, isRoot, context) {
263278
const children: Array<VNode> = el.children
264279
context.renderStates.push({
265280
type: 'Element',
281+
children,
266282
rendered: 0,
267283
total: children.length,
268-
endTag, children
284+
endTag
269285
})
270286
write(startTag, next)
271287
}

test/ssr/ssr-string.spec.js

+54
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,60 @@ describe('SSR: renderToString', () => {
575575
})
576576
})
577577

578+
it('renders async component (functional, single node)', done => {
579+
renderVmWithOptions({
580+
template: `
581+
<div>
582+
<test-async></test-async>
583+
</div>
584+
`,
585+
components: {
586+
testAsync (resolve) {
587+
setTimeout(() => resolve({
588+
functional: true,
589+
render (h) {
590+
return h('span', { class: ['b'] }, 'testAsync')
591+
}
592+
}), 1)
593+
}
594+
}
595+
}, result => {
596+
expect(result).toContain('<div data-server-rendered="true"><span class="b">testAsync</span></div>')
597+
done()
598+
})
599+
})
600+
601+
it('renders async component (functional, multiple nodes)', done => {
602+
renderVmWithOptions({
603+
template: `
604+
<div>
605+
<test-async></test-async>
606+
</div>
607+
`,
608+
components: {
609+
testAsync (resolve) {
610+
setTimeout(() => resolve({
611+
functional: true,
612+
render (h) {
613+
return [
614+
h('span', { class: ['a'] }, 'foo'),
615+
h('span', { class: ['b'] }, 'bar')
616+
]
617+
}
618+
}), 1)
619+
}
620+
}
621+
}, result => {
622+
expect(result).toContain(
623+
'<div data-server-rendered="true">' +
624+
'<span class="a">foo</span>' +
625+
'<span class="b">bar</span>' +
626+
'</div>'
627+
)
628+
done()
629+
})
630+
})
631+
578632
it('should catch async component error', done => {
579633
Vue.config.silent = true
580634
renderToString(new Vue({

0 commit comments

Comments
 (0)