Skip to content

Commit 7404091

Browse files
committed
support async hydration
1 parent f3757eb commit 7404091

File tree

5 files changed

+50
-14
lines changed

5 files changed

+50
-14
lines changed

src/core/vdom/create-component.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515

1616
import {
1717
resolveAsyncComponent,
18+
createAsyncPlaceholder,
1819
extractPropsFromVNodeData
1920
} from './helpers/index'
2021

@@ -122,22 +123,24 @@ export function createComponent (
122123
return
123124
}
124125

126+
data = data || {}
127+
125128
// async component
129+
let asyncFactory
126130
if (isUndef(Ctor.cid)) {
127-
Ctor = resolveAsyncComponent(Ctor, baseCtor, context)
131+
asyncFactory = Ctor
132+
Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
128133
if (Ctor === undefined) {
129134
// return nothing if this is indeed an async component
130135
// wait for the callback to trigger parent update.
131-
return
136+
return createAsyncPlaceholder(asyncFactory, data.key)
132137
}
133138
}
134139

135140
// resolve constructor options in case global mixins are applied after
136141
// component constructor creation
137142
resolveConstructorOptions(Ctor)
138143

139-
data = data || {}
140-
141144
// transform component v-model data into props & events
142145
if (isDef(data.model)) {
143146
transformModel(Ctor.options, data)
@@ -171,7 +174,8 @@ export function createComponent (
171174
const vnode = new VNode(
172175
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
173176
data, undefined, undefined, undefined, context,
174-
{ Ctor, propsData, listeners, tag, children }
177+
{ Ctor, propsData, listeners, tag, children },
178+
asyncFactory
175179
)
176180
return vnode
177181
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,24 @@ import {
99
isObject
1010
} from 'core/util/index'
1111

12+
import { createEmptyVNode } from 'core/vdom/vnode'
13+
1214
function ensureCtor (comp, base) {
1315
return isObject(comp)
1416
? base.extend(comp)
1517
: comp
1618
}
1719

20+
export function createAsyncPlaceholder (
21+
factory: Function,
22+
key: string | number | void
23+
): VNode {
24+
const node = createEmptyVNode()
25+
node.asyncFactory = factory
26+
node.key = key
27+
return node
28+
}
29+
1830
export function resolveAsyncComponent (
1931
factory: Function,
2032
baseCtor: Class<Component>,

src/core/vdom/patch.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,17 @@ const hooks = ['create', 'activate', 'update', 'remove', 'destroy']
3333

3434
function sameVnode (a, b) {
3535
return (
36-
a.key === b.key &&
37-
a.tag === b.tag &&
38-
a.isComment === b.isComment &&
39-
isDef(a.data) === isDef(b.data) &&
40-
sameInputType(a, b)
36+
a.key === b.key && (
37+
(
38+
a.isAsyncPlaceholder === true &&
39+
a.asyncFactory === b.asyncFactory
40+
) || (
41+
a.tag === b.tag &&
42+
a.isComment === b.isComment &&
43+
isDef(a.data) === isDef(b.data) &&
44+
sameInputType(a, b)
45+
)
46+
)
4147
)
4248
}
4349

@@ -434,6 +440,9 @@ export function createPatchFunction (backend) {
434440
if (oldVnode === vnode) {
435441
return
436442
}
443+
if (oldVnode.isAsyncPlaceholder) {
444+
return hydrate(oldVnode.elm, vnode, insertedVnodeQueue)
445+
}
437446
// reuse element for static trees.
438447
// note we only do this if the vnode is cloned -
439448
// if the new node is not cloned it means the render functions have been
@@ -497,6 +506,11 @@ export function createPatchFunction (backend) {
497506

498507
// Note: this is a browser-only function so we can assume elms are DOM nodes.
499508
function hydrate (elm, vnode, insertedVnodeQueue) {
509+
if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) {
510+
vnode.elm = elm
511+
vnode.isAsyncPlaceholder = true
512+
return true
513+
}
500514
if (process.env.NODE_ENV !== 'production') {
501515
if (!assertNodeMatch(elm, vnode)) {
502516
return false

src/core/vdom/vnode.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export default class VNode {
1919
isComment: boolean; // empty comment placeholder?
2020
isCloned: boolean; // is a cloned node?
2121
isOnce: boolean; // is a v-once node?
22+
asyncFactory: ?Function; // async component factory function
23+
isAsyncPlaceholder: boolean;
2224

2325
constructor (
2426
tag?: string,
@@ -27,7 +29,8 @@ export default class VNode {
2729
text?: string,
2830
elm?: Node,
2931
context?: Component,
30-
componentOptions?: VNodeComponentOptions
32+
componentOptions?: VNodeComponentOptions,
33+
asyncFactory?: Function
3134
) {
3235
this.tag = tag
3336
this.data = data
@@ -47,6 +50,8 @@ export default class VNode {
4750
this.isComment = false
4851
this.isCloned = false
4952
this.isOnce = false
53+
this.asyncFactory = asyncFactory
54+
this.isAsyncPlaceholder = false
5055
}
5156

5257
// DEPRECATED: alias for componentInstance for backwards compat.

test/unit/modules/vdom/create-component.spec.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ describe('create-component', () => {
6161
}
6262
function go () {
6363
vnode = createComponent(async, data, vm, vm)
64-
expect(vnode).toBeUndefined() // not to be loaded yet.
64+
expect(vnode.isComment).toBe(true) // not to be loaded yet.
65+
expect(vnode.asyncFactory).toBe(async)
6566
}
6667
function loaded () {
6768
vnode = createComponent(async, data, vm, vm)
@@ -93,11 +94,11 @@ describe('create-component', () => {
9394
}
9495
function go () {
9596
vnode = createComponent(async, data, vm, vm)
96-
expect(vnode).toBeUndefined() // not to be loaded yet.
97+
expect(vnode.isComment).toBe(true) // not to be loaded yet.
9798
}
9899
function failed () {
99100
vnode = createComponent(async, data, vm, vm)
100-
expect(vnode).toBeUndefined() // failed
101+
expect(vnode.isComment).toBe(true) // failed, still a comment node
101102
expect(`Failed to resolve async component: ${async}\nReason: ${reason}`).toHaveBeenWarned()
102103
done()
103104
}

0 commit comments

Comments
 (0)