Browse Source

client-side hydration

dev
Evan You 9 years ago
parent
commit
c8e91ab24e
  1. 6
      src/core/vdom/create-component.js
  2. 85
      src/core/vdom/patch.js
  3. 2
      src/platforms/web/util/attrs.js

6
src/core/vdom/create-component.js

@ -74,9 +74,11 @@ function init (vnode) {
_parentListeners: listeners, _parentListeners: listeners,
_renderChildren: children _renderChildren: children
}) })
// the child sets the parent vnode's elm when mounted // if this is a server-rendered mount,
// the vnode would already have an element.
// otherwise the child sets the parent vnode's elm when mounted
// and when updated. // and when updated.
child.$mount() child.$mount(vnode.elm)
vnode.child = child vnode.child = child
} }

85
src/core/vdom/patch.js

@ -8,7 +8,7 @@ import VNode from './vnode'
import { isPrimitive, warn } from '../util/index' import { isPrimitive, warn } from '../util/index'
const emptyNode = VNode('', {}, []) const emptyNode = VNode('', {}, [])
const hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post'] const hooks = ['create', 'update', 'remove', 'destroy']
function isUndef (s) { function isUndef (s) {
return s === undefined return s === undefined
@ -283,10 +283,64 @@ export function createPatchFunction (backend) {
} }
} }
function invokeInsertHook (queue) {
for (i = 0; i < queue.length; ++i) {
queue[i].data.hook.insert(queue[i])
}
}
function hydrate (elm, vnode, insertedVnodeQueue) {
if (process.env.NODE_ENV !== 'production') {
if (!assertNodeMatch(elm, vnode)) {
return false
}
}
vnode.elm = elm
const { tag, data, children } = vnode
if (isDef(data)) {
if (isDef(i = data.hook) && isDef(i = i.init)) i(vnode)
if (isDef(i = vnode.child)) {
// child component. it should have hydrated its own tree.
invokeCreateHooks(vnode, insertedVnodeQueue)
return true
}
}
if (isDef(tag)) {
if (isDef(children)) {
const childNodes = elm.childNodes
for (let i = 0; i < children.length; i++) {
let success = hydrate(childNodes[i], children[i], insertedVnodeQueue)
if (!success) {
return false
}
}
}
if (isDef(data)) {
invokeCreateHooks(vnode, insertedVnodeQueue)
}
}
return true
}
function assertNodeMatch (node, vnode) {
if (vnode.tag) {
if (vnode.tag.indexOf('vue-component' === 0)) {
return true
} else {
return vnode.tag === node.tagName.toLowerCase() && (
vnode.children
? vnode.children.length === node.childNodes.length
: node.childNodes.length === 0
)
}
} else {
return vnode.text === node.textContent
}
}
return function patch (oldVnode, vnode) { return function patch (oldVnode, vnode) {
var i, elm, parent var elm, parent
var insertedVnodeQueue = [] var insertedVnodeQueue = []
for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]()
if (!oldVnode) { if (!oldVnode) {
// empty mount, create new root element // empty mount, create new root element
@ -295,9 +349,25 @@ export function createPatchFunction (backend) {
if (sameVnode(oldVnode, vnode)) { if (sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode, insertedVnodeQueue) patchVnode(oldVnode, vnode, insertedVnodeQueue)
} else { } else {
// mounting to a real element, just replace it
// TODO: pick up and reuse existing elements (hydration)
if (isUndef(oldVnode.tag)) { if (isUndef(oldVnode.tag)) {
// mounting to a real element
// check if this is server-rendered content and if we can perform
// a successful hydration.
if (oldVnode.hasAttribute('server-rendered')) {
oldVnode.removeAttribute('server-rendered')
if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {
invokeInsertHook(insertedVnodeQueue)
return oldVnode
} else if (process.env.NODE_ENV !== 'production') {
warn(
'The client-side rendered virtual DOM tree is not matching ' +
'server-rendered content. Bailing hydration and performing ' +
'full client-side render.'
)
}
}
// either not server-rendered, or hydration failed.
// create an empty node and replace it
oldVnode = emptyNodeAt(oldVnode) oldVnode = emptyNodeAt(oldVnode)
} }
elm = oldVnode.elm elm = oldVnode.elm
@ -312,10 +382,7 @@ export function createPatchFunction (backend) {
} }
} }
for (i = 0; i < insertedVnodeQueue.length; ++i) { invokeInsertHook(insertedVnodeQueue)
insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i])
}
for (i = 0; i < cbs.post.length; ++i) cbs.post[i]()
return vnode.elm return vnode.elm
} }
} }

2
src/platforms/web/util/attrs.js

@ -18,7 +18,7 @@ export const propsToAttrMap = {
acceptCharset: 'accept-charset', acceptCharset: 'accept-charset',
className: 'class', className: 'class',
htmlFor: 'for', htmlFor: 'for',
httpEquiv: 'http-equiv', httpEquiv: 'http-equiv'
} }
export const xlinkNS = 'http://www.w3.org/1999/xlink' export const xlinkNS = 'http://www.w3.org/1999/xlink'

Loading…
Cancel
Save