Skip to content

Commit

Permalink
support async hydration
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed May 24, 2017
1 parent f3757eb commit 7404091
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 14 deletions.
14 changes: 9 additions & 5 deletions src/core/vdom/create-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {

import {
resolveAsyncComponent,
createAsyncPlaceholder,
extractPropsFromVNodeData
} from './helpers/index'

Expand Down Expand Up @@ -122,22 +123,24 @@ export function createComponent (
return
}

data = data || {}

// async component
let asyncFactory
if (isUndef(Ctor.cid)) {
Ctor = resolveAsyncComponent(Ctor, baseCtor, context)
asyncFactory = Ctor
Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
if (Ctor === undefined) {
// return nothing if this is indeed an async component
// wait for the callback to trigger parent update.
return
return createAsyncPlaceholder(asyncFactory, data.key)
}
}

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

data = data || {}

// transform component v-model data into props & events
if (isDef(data.model)) {
transformModel(Ctor.options, data)
Expand Down Expand Up @@ -171,7 +174,8 @@ export function createComponent (
const vnode = new VNode(
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
data, undefined, undefined, undefined, context,
{ Ctor, propsData, listeners, tag, children }
{ Ctor, propsData, listeners, tag, children },
asyncFactory
)
return vnode
}
Expand Down
12 changes: 12 additions & 0 deletions src/core/vdom/helpers/resolve-async-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,24 @@ import {
isObject
} from 'core/util/index'

import { createEmptyVNode } from 'core/vdom/vnode'

function ensureCtor (comp, base) {
return isObject(comp)
? base.extend(comp)
: comp
}

export function createAsyncPlaceholder (
factory: Function,
key: string | number | void
): VNode {
const node = createEmptyVNode()
node.asyncFactory = factory
node.key = key
return node
}

export function resolveAsyncComponent (
factory: Function,
baseCtor: Class<Component>,
Expand Down
24 changes: 19 additions & 5 deletions src/core/vdom/patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,17 @@ const hooks = ['create', 'activate', 'update', 'remove', 'destroy']

function sameVnode (a, b) {
return (
a.key === b.key &&
a.tag === b.tag &&
a.isComment === b.isComment &&
isDef(a.data) === isDef(b.data) &&
sameInputType(a, b)
a.key === b.key && (
(
a.isAsyncPlaceholder === true &&
a.asyncFactory === b.asyncFactory
) || (
a.tag === b.tag &&
a.isComment === b.isComment &&
isDef(a.data) === isDef(b.data) &&
sameInputType(a, b)
)
)
)
}

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

// Note: this is a browser-only function so we can assume elms are DOM nodes.
function hydrate (elm, vnode, insertedVnodeQueue) {
if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) {
vnode.elm = elm
vnode.isAsyncPlaceholder = true
return true
}
if (process.env.NODE_ENV !== 'production') {
if (!assertNodeMatch(elm, vnode)) {
return false
Expand Down
7 changes: 6 additions & 1 deletion src/core/vdom/vnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export default class VNode {
isComment: boolean; // empty comment placeholder?
isCloned: boolean; // is a cloned node?
isOnce: boolean; // is a v-once node?
asyncFactory: ?Function; // async component factory function
isAsyncPlaceholder: boolean;

constructor (
tag?: string,
Expand All @@ -27,7 +29,8 @@ export default class VNode {
text?: string,
elm?: Node,
context?: Component,
componentOptions?: VNodeComponentOptions
componentOptions?: VNodeComponentOptions,
asyncFactory?: Function
) {
this.tag = tag
this.data = data
Expand All @@ -47,6 +50,8 @@ export default class VNode {
this.isComment = false
this.isCloned = false
this.isOnce = false
this.asyncFactory = asyncFactory
this.isAsyncPlaceholder = false
}

// DEPRECATED: alias for componentInstance for backwards compat.
Expand Down
7 changes: 4 additions & 3 deletions test/unit/modules/vdom/create-component.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ describe('create-component', () => {
}
function go () {
vnode = createComponent(async, data, vm, vm)
expect(vnode).toBeUndefined() // not to be loaded yet.
expect(vnode.isComment).toBe(true) // not to be loaded yet.
expect(vnode.asyncFactory).toBe(async)
}
function loaded () {
vnode = createComponent(async, data, vm, vm)
Expand Down Expand Up @@ -93,11 +94,11 @@ describe('create-component', () => {
}
function go () {
vnode = createComponent(async, data, vm, vm)
expect(vnode).toBeUndefined() // not to be loaded yet.
expect(vnode.isComment).toBe(true) // not to be loaded yet.
}
function failed () {
vnode = createComponent(async, data, vm, vm)
expect(vnode).toBeUndefined() // failed
expect(vnode.isComment).toBe(true) // failed, still a comment node
expect(`Failed to resolve async component: ${async}\nReason: ${reason}`).toHaveBeenWarned()
done()
}
Expand Down

0 comments on commit 7404091

Please sign in to comment.