Skip to content

Commit 9cf6646

Browse files
committed
support directly rendering async components in SSR
1 parent 7404091 commit 9cf6646

File tree

4 files changed

+93
-22
lines changed

4 files changed

+93
-22
lines changed

src/core/vdom/create-component.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ const hooksToMerge = Object.keys(componentVNodeHooks)
9898

9999
export function createComponent (
100100
Ctor: Class<Component> | Function | Object | void,
101-
data?: VNodeData,
101+
data: ?VNodeData,
102102
context: Component,
103103
children: ?Array<VNode>,
104104
tag?: string
@@ -123,20 +123,27 @@ export function createComponent (
123123
return
124124
}
125125

126-
data = data || {}
127-
128126
// async component
129127
let asyncFactory
130128
if (isUndef(Ctor.cid)) {
131129
asyncFactory = Ctor
132130
Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
133131
if (Ctor === undefined) {
134-
// return nothing if this is indeed an async component
135-
// wait for the callback to trigger parent update.
136-
return createAsyncPlaceholder(asyncFactory, data.key)
132+
// return a placeholder node for async component, which is rendered
133+
// as a comment node but preserves all the raw information for the node.
134+
// the information will be used for async server-rendering and hydration.
135+
return createAsyncPlaceholder(
136+
asyncFactory,
137+
data,
138+
context,
139+
children,
140+
tag
141+
)
137142
}
138143
}
139144

145+
data = data || {}
146+
140147
// resolve constructor options in case global mixins are applied after
141148
// component constructor creation
142149
resolveConstructorOptions(Ctor)

src/core/vdom/helpers/resolve-async-component.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ function ensureCtor (comp, base) {
1919

2020
export function createAsyncPlaceholder (
2121
factory: Function,
22-
key: string | number | void
22+
data: ?VNodeData,
23+
context: Component,
24+
children: ?Array<VNode>,
25+
tag: ?string
2326
): VNode {
2427
const node = createEmptyVNode()
2528
node.asyncFactory = factory
26-
node.key = key
29+
node.asyncMeta = { data, context, children, tag }
2730
return node
2831
}
2932

src/core/vdom/vnode.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ export default class VNode {
2020
isCloned: boolean; // is a cloned node?
2121
isOnce: boolean; // is a v-once node?
2222
asyncFactory: ?Function; // async component factory function
23+
asyncMeta: ?Object;
2324
isAsyncPlaceholder: boolean;
25+
ssrContext: ?Object;
2426

2527
constructor (
2628
tag?: string,
@@ -51,6 +53,7 @@ export default class VNode {
5153
this.isCloned = false
5254
this.isOnce = false
5355
this.asyncFactory = asyncFactory
56+
this.asyncMeta = undefined
5457
this.isAsyncPlaceholder = false
5558
}
5659

src/server/render.js

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
/* @flow */
22

3+
import {
4+
isDef,
5+
isUndef,
6+
isTrue
7+
} from 'shared/util'
8+
39
import { escape } from 'web/server/util'
410
import { SSR_ATTR } from 'shared/constants'
511
import { RenderContext } from './render-context'
612
import { ssrCompileToFunctions } from 'web/server/compiler'
713
import { installSSRHelpers } from './optimizing-compiler/runtime-helpers'
8-
import { createComponentInstanceForVnode } from 'core/vdom/create-component'
914

10-
import { isDef, isUndef, isTrue } from 'shared/util'
15+
import {
16+
createComponent,
17+
createComponentInstanceForVnode
18+
} from 'core/vdom/create-component'
1119

1220
let warned = Object.create(null)
1321
const warnOnce = msg => {
@@ -39,20 +47,20 @@ function renderNode (node, isRoot, context) {
3947
renderStringNode(node, context)
4048
} else if (isDef(node.componentOptions)) {
4149
renderComponent(node, isRoot, context)
42-
} else {
43-
if (isDef(node.tag)) {
44-
renderElement(node, isRoot, context)
45-
} else if (isTrue(node.isComment)) {
46-
context.write(
47-
`<!--${node.text}-->`,
48-
context.next
49-
)
50+
} else if (isDef(node.tag)) {
51+
renderElement(node, isRoot, context)
52+
} else if (isTrue(node.isComment)) {
53+
if (isDef(node.asyncFactory)) {
54+
// async component
55+
renderAsyncComponent(node, isRoot, context)
5056
} else {
51-
context.write(
52-
node.raw ? node.text : escape(String(node.text)),
53-
context.next
54-
)
57+
context.write(`<!--${node.text}-->`, context.next)
5558
}
59+
} else {
60+
context.write(
61+
node.raw ? node.text : escape(String(node.text)),
62+
context.next
63+
)
5664
}
5765
}
5866

@@ -160,6 +168,56 @@ function renderComponentInner (node, isRoot, context) {
160168
renderNode(childNode, isRoot, context)
161169
}
162170

171+
function renderAsyncComponent (node, isRoot, context) {
172+
const factory = node.asyncFactory
173+
174+
const resolve = comp => {
175+
const { data, children, tag } = node.asyncMeta
176+
const nodeContext = node.asyncMeta.context
177+
const resolvedNode: any = createComponent(
178+
comp,
179+
data,
180+
nodeContext,
181+
children,
182+
tag
183+
)
184+
if (resolvedNode) {
185+
renderComponent(resolvedNode, isRoot, context)
186+
} else {
187+
reject()
188+
}
189+
}
190+
191+
const reject = err => {
192+
console.error(`[vue-server-renderer] error when rendering async component:\n`)
193+
if (err) console.error(err.stack)
194+
context.write(`<!--${node.text}-->`, context.next)
195+
}
196+
197+
if (factory.resolved) {
198+
resolve(factory.resolved)
199+
return
200+
}
201+
202+
let res
203+
try {
204+
res = factory(resolve, reject)
205+
} catch (e) {
206+
reject(e)
207+
}
208+
if (res) {
209+
if (typeof res.then === 'function') {
210+
res.then(resolve, reject).catch(reject)
211+
} else {
212+
// new syntax in 2.3
213+
const comp = res.component
214+
if (comp && typeof comp.then === 'function') {
215+
comp.then(resolve, reject).catch(reject)
216+
}
217+
}
218+
}
219+
}
220+
163221
function renderStringNode (el, context) {
164222
const { write, next } = context
165223
if (isUndef(el.children) || el.children.length === 0) {

0 commit comments

Comments
 (0)