2020-02-16 00:40:09 +08:00
|
|
|
import { VNode, normalizeVNode, Text, Comment, Static, Fragment } from './vnode'
|
2020-03-13 10:19:41 +08:00
|
|
|
import { flushPostFlushCbs } from './scheduler'
|
2020-02-14 12:31:03 +08:00
|
|
|
import { ComponentInternalInstance } from './component'
|
|
|
|
import { invokeDirectiveHook } from './directives'
|
|
|
|
import { warn } from './warning'
|
2020-02-15 10:04:08 +08:00
|
|
|
import {
|
|
|
|
PatchFlags,
|
|
|
|
ShapeFlags,
|
|
|
|
isReservedProp,
|
|
|
|
isOn,
|
|
|
|
isString
|
|
|
|
} from '@vue/shared'
|
2020-02-16 00:40:09 +08:00
|
|
|
import { RendererInternals } from './renderer'
|
2020-03-13 10:19:41 +08:00
|
|
|
import {
|
|
|
|
SuspenseImpl,
|
|
|
|
SuspenseBoundary,
|
|
|
|
queueEffectWithSuspense
|
|
|
|
} from './components/Suspense'
|
2020-02-14 12:31:03 +08:00
|
|
|
|
2020-02-15 01:33:32 +08:00
|
|
|
export type RootHydrateFunction = (
|
|
|
|
vnode: VNode<Node, Element>,
|
|
|
|
container: Element
|
|
|
|
) => void
|
|
|
|
|
2020-03-04 05:12:38 +08:00
|
|
|
const enum DOMNodeTypes {
|
|
|
|
ELEMENT = 1,
|
|
|
|
TEXT = 3,
|
|
|
|
COMMENT = 8
|
|
|
|
}
|
|
|
|
|
2020-03-05 07:06:50 +08:00
|
|
|
let hasMismatch = false
|
2020-03-04 05:12:38 +08:00
|
|
|
|
2020-03-13 10:19:41 +08:00
|
|
|
const isSVGContainer = (container: Element) =>
|
|
|
|
/svg/.test(container.namespaceURI!) && container.tagName !== 'foreignObject'
|
|
|
|
|
|
|
|
const isComment = (node: Node): node is Comment =>
|
|
|
|
node.nodeType === DOMNodeTypes.COMMENT
|
|
|
|
|
2020-02-14 12:31:03 +08:00
|
|
|
// Note: hydration is DOM-specific
|
2020-02-14 14:30:08 +08:00
|
|
|
// But we have to place it in core due to tight coupling with core - splitting
|
2020-02-14 13:13:54 +08:00
|
|
|
// it out creates a ton of unnecessary complexity.
|
2020-02-14 14:30:08 +08:00
|
|
|
// Hydration also depends on some renderer internal logic which needs to be
|
|
|
|
// passed in via arguments.
|
2020-03-13 10:19:41 +08:00
|
|
|
export function createHydrationFunctions(
|
|
|
|
rendererInternals: RendererInternals<Node, Element>
|
|
|
|
) {
|
|
|
|
const {
|
|
|
|
mt: mountComponent,
|
|
|
|
p: patch,
|
|
|
|
n: next,
|
|
|
|
o: { patchProp, nextSibling, parentNode }
|
|
|
|
} = rendererInternals
|
|
|
|
|
2020-02-15 01:33:32 +08:00
|
|
|
const hydrate: RootHydrateFunction = (vnode, container) => {
|
2020-02-14 12:31:03 +08:00
|
|
|
if (__DEV__ && !container.hasChildNodes()) {
|
2020-03-04 05:12:38 +08:00
|
|
|
warn(
|
|
|
|
`Attempting to hydrate existing markup but container is empty. ` +
|
|
|
|
`Performing full mount instead.`
|
|
|
|
)
|
|
|
|
patch(null, vnode, container)
|
2020-02-14 12:31:03 +08:00
|
|
|
return
|
|
|
|
}
|
2020-03-05 07:06:50 +08:00
|
|
|
hasMismatch = false
|
2020-03-13 10:19:41 +08:00
|
|
|
hydrateNode(container.firstChild!, vnode, null, null)
|
2020-02-14 12:31:03 +08:00
|
|
|
flushPostFlushCbs()
|
2020-03-05 07:06:50 +08:00
|
|
|
if (hasMismatch && !__TEST__) {
|
2020-03-04 05:12:38 +08:00
|
|
|
// this error should show up in production
|
|
|
|
console.error(`Hydration completed but contains mismatches.`)
|
|
|
|
}
|
2020-02-14 12:31:03 +08:00
|
|
|
}
|
|
|
|
|
2020-02-14 16:22:52 +08:00
|
|
|
const hydrateNode = (
|
2020-02-14 12:31:03 +08:00
|
|
|
node: Node,
|
|
|
|
vnode: VNode,
|
2020-03-13 10:19:41 +08:00
|
|
|
parentComponent: ComponentInternalInstance | null,
|
|
|
|
parentSuspense: SuspenseBoundary | null,
|
2020-02-28 00:26:39 +08:00
|
|
|
optimized = false
|
2020-02-27 05:32:06 +08:00
|
|
|
): Node | null => {
|
2020-02-14 12:31:03 +08:00
|
|
|
const { type, shapeFlag } = vnode
|
2020-03-04 05:12:38 +08:00
|
|
|
const domType = node.nodeType
|
|
|
|
|
2020-02-14 12:31:03 +08:00
|
|
|
vnode.el = node
|
2020-03-04 05:12:38 +08:00
|
|
|
|
2020-02-14 12:31:03 +08:00
|
|
|
switch (type) {
|
|
|
|
case Text:
|
2020-03-04 05:12:38 +08:00
|
|
|
if (domType !== DOMNodeTypes.TEXT) {
|
2020-03-13 10:19:41 +08:00
|
|
|
return handleMismtach(node, vnode, parentComponent, parentSuspense)
|
2020-03-04 05:12:38 +08:00
|
|
|
}
|
|
|
|
if ((node as Text).data !== vnode.children) {
|
2020-03-05 07:06:50 +08:00
|
|
|
hasMismatch = true
|
2020-03-04 05:12:38 +08:00
|
|
|
__DEV__ &&
|
|
|
|
warn(
|
|
|
|
`Hydration text mismatch:` +
|
|
|
|
`\n- Client: ${JSON.stringify(vnode.children)}`,
|
|
|
|
`\n- Server: ${JSON.stringify((node as Text).data)}`
|
|
|
|
)
|
|
|
|
;(node as Text).data = vnode.children as string
|
|
|
|
}
|
2020-03-13 10:19:41 +08:00
|
|
|
return nextSibling(node)
|
2020-02-14 12:31:03 +08:00
|
|
|
case Comment:
|
2020-03-04 05:12:38 +08:00
|
|
|
if (domType !== DOMNodeTypes.COMMENT) {
|
2020-03-13 10:19:41 +08:00
|
|
|
return handleMismtach(node, vnode, parentComponent, parentSuspense)
|
2020-03-04 05:12:38 +08:00
|
|
|
}
|
2020-03-13 10:19:41 +08:00
|
|
|
return nextSibling(node)
|
2020-02-14 12:31:03 +08:00
|
|
|
case Static:
|
2020-03-04 05:12:38 +08:00
|
|
|
if (domType !== DOMNodeTypes.ELEMENT) {
|
2020-03-13 10:19:41 +08:00
|
|
|
return handleMismtach(node, vnode, parentComponent, parentSuspense)
|
2020-03-04 05:12:38 +08:00
|
|
|
}
|
2020-03-13 10:19:41 +08:00
|
|
|
return nextSibling(node)
|
2020-02-14 12:31:03 +08:00
|
|
|
case Fragment:
|
2020-03-13 10:19:41 +08:00
|
|
|
if (domType !== DOMNodeTypes.COMMENT) {
|
|
|
|
return handleMismtach(node, vnode, parentComponent, parentSuspense)
|
|
|
|
}
|
|
|
|
return hydrateFragment(
|
|
|
|
node as Comment,
|
|
|
|
vnode,
|
|
|
|
parentComponent,
|
|
|
|
parentSuspense,
|
|
|
|
optimized
|
|
|
|
)
|
2020-02-14 12:31:03 +08:00
|
|
|
default:
|
|
|
|
if (shapeFlag & ShapeFlags.ELEMENT) {
|
2020-03-07 04:39:54 +08:00
|
|
|
if (
|
|
|
|
domType !== DOMNodeTypes.ELEMENT ||
|
|
|
|
vnode.type !== (node as Element).tagName.toLowerCase()
|
|
|
|
) {
|
2020-03-13 10:19:41 +08:00
|
|
|
return handleMismtach(node, vnode, parentComponent, parentSuspense)
|
2020-03-04 05:12:38 +08:00
|
|
|
}
|
2020-02-28 00:26:39 +08:00
|
|
|
return hydrateElement(
|
|
|
|
node as Element,
|
|
|
|
vnode,
|
|
|
|
parentComponent,
|
2020-03-13 10:19:41 +08:00
|
|
|
parentSuspense,
|
2020-02-28 00:26:39 +08:00
|
|
|
optimized
|
|
|
|
)
|
2020-02-14 12:31:03 +08:00
|
|
|
} else if (shapeFlag & ShapeFlags.COMPONENT) {
|
2020-02-15 05:25:41 +08:00
|
|
|
// when setting up the render effect, if the initial vnode already
|
|
|
|
// has .el set, the component will perform hydration instead of mount
|
|
|
|
// on its sub-tree.
|
2020-03-13 10:19:41 +08:00
|
|
|
const container = parentNode(node)!
|
|
|
|
mountComponent(
|
|
|
|
vnode,
|
|
|
|
container,
|
|
|
|
null,
|
|
|
|
parentComponent,
|
|
|
|
parentSuspense,
|
|
|
|
isSVGContainer(container)
|
|
|
|
)
|
2020-02-14 12:31:03 +08:00
|
|
|
const subTree = vnode.component!.subTree
|
2020-03-13 10:19:41 +08:00
|
|
|
if (subTree) {
|
|
|
|
return next(subTree)
|
|
|
|
} else {
|
|
|
|
// no subTree means this is an async component
|
|
|
|
// try to locate the ending node
|
|
|
|
return isComment(node) && node.data === '1'
|
|
|
|
? locateClosingAsyncAnchor(node)
|
|
|
|
: nextSibling(node)
|
|
|
|
}
|
2020-02-16 00:40:09 +08:00
|
|
|
} else if (shapeFlag & ShapeFlags.PORTAL) {
|
2020-03-04 05:12:38 +08:00
|
|
|
if (domType !== DOMNodeTypes.COMMENT) {
|
2020-03-13 10:19:41 +08:00
|
|
|
return handleMismtach(node, vnode, parentComponent, parentSuspense)
|
2020-03-04 05:12:38 +08:00
|
|
|
}
|
2020-03-13 10:19:41 +08:00
|
|
|
hydratePortal(vnode, parentComponent, parentSuspense, optimized)
|
|
|
|
return nextSibling(node)
|
2020-02-14 12:31:03 +08:00
|
|
|
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
|
2020-03-13 10:19:41 +08:00
|
|
|
return (vnode.type as typeof SuspenseImpl).hydrate(
|
|
|
|
node,
|
|
|
|
vnode,
|
|
|
|
parentComponent,
|
|
|
|
parentSuspense,
|
|
|
|
isSVGContainer(parentNode(node)!),
|
|
|
|
optimized,
|
|
|
|
rendererInternals,
|
|
|
|
hydrateNode
|
|
|
|
)
|
2020-02-14 12:31:03 +08:00
|
|
|
} else if (__DEV__) {
|
|
|
|
warn('Invalid HostVNode type:', type, `(${typeof type})`)
|
|
|
|
}
|
2020-02-27 05:32:06 +08:00
|
|
|
return null
|
2020-02-14 12:31:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-14 16:22:52 +08:00
|
|
|
const hydrateElement = (
|
2020-02-14 12:31:03 +08:00
|
|
|
el: Element,
|
|
|
|
vnode: VNode,
|
2020-02-28 00:26:39 +08:00
|
|
|
parentComponent: ComponentInternalInstance | null,
|
2020-03-13 10:19:41 +08:00
|
|
|
parentSuspense: SuspenseBoundary | null,
|
2020-02-28 00:26:39 +08:00
|
|
|
optimized: boolean
|
2020-02-14 16:22:52 +08:00
|
|
|
) => {
|
2020-03-06 00:29:50 +08:00
|
|
|
optimized = optimized || vnode.dynamicChildren !== null
|
2020-03-04 05:12:38 +08:00
|
|
|
const { props, patchFlag, shapeFlag } = vnode
|
2020-02-14 12:31:03 +08:00
|
|
|
// skip props & children if this is hoisted static nodes
|
|
|
|
if (patchFlag !== PatchFlags.HOISTED) {
|
|
|
|
// props
|
|
|
|
if (props !== null) {
|
|
|
|
if (
|
2020-03-06 00:29:50 +08:00
|
|
|
!optimized ||
|
|
|
|
(patchFlag & PatchFlags.FULL_PROPS ||
|
|
|
|
patchFlag & PatchFlags.HYDRATE_EVENTS)
|
2020-02-14 12:31:03 +08:00
|
|
|
) {
|
|
|
|
for (const key in props) {
|
|
|
|
if (!isReservedProp(key) && isOn(key)) {
|
2020-03-10 04:15:49 +08:00
|
|
|
patchProp(el, key, null, props[key])
|
2020-02-14 12:31:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (props.onClick != null) {
|
|
|
|
// Fast path for click listeners (which is most often) to avoid
|
|
|
|
// iterating through props.
|
2020-03-10 04:15:49 +08:00
|
|
|
patchProp(el, 'onClick', null, props.onClick)
|
2020-02-14 12:31:03 +08:00
|
|
|
}
|
|
|
|
// vnode hooks
|
|
|
|
const { onVnodeBeforeMount, onVnodeMounted } = props
|
|
|
|
if (onVnodeBeforeMount != null) {
|
|
|
|
invokeDirectiveHook(onVnodeBeforeMount, parentComponent, vnode)
|
|
|
|
}
|
|
|
|
if (onVnodeMounted != null) {
|
2020-03-13 10:19:41 +08:00
|
|
|
queueEffectWithSuspense(() => {
|
2020-02-14 12:31:03 +08:00
|
|
|
invokeDirectiveHook(onVnodeMounted, parentComponent, vnode)
|
2020-03-13 10:19:41 +08:00
|
|
|
}, parentSuspense)
|
2020-02-14 12:31:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// children
|
|
|
|
if (
|
2020-03-04 05:12:38 +08:00
|
|
|
shapeFlag & ShapeFlags.ARRAY_CHILDREN &&
|
2020-02-14 12:31:03 +08:00
|
|
|
// skip if element has innerHTML / textContent
|
|
|
|
!(props !== null && (props.innerHTML || props.textContent))
|
|
|
|
) {
|
2020-03-04 05:12:38 +08:00
|
|
|
let next = hydrateChildren(
|
2020-02-14 12:31:03 +08:00
|
|
|
el.firstChild,
|
2020-02-28 00:26:39 +08:00
|
|
|
vnode,
|
2020-03-04 05:12:38 +08:00
|
|
|
el,
|
2020-02-28 00:26:39 +08:00
|
|
|
parentComponent,
|
2020-03-13 10:19:41 +08:00
|
|
|
parentSuspense,
|
2020-03-06 00:29:50 +08:00
|
|
|
optimized
|
2020-02-14 12:31:03 +08:00
|
|
|
)
|
2020-03-07 04:39:54 +08:00
|
|
|
let hasWarned = false
|
2020-03-04 05:12:38 +08:00
|
|
|
while (next) {
|
2020-03-05 07:06:50 +08:00
|
|
|
hasMismatch = true
|
2020-03-07 04:39:54 +08:00
|
|
|
if (__DEV__ && !hasWarned) {
|
2020-03-04 05:12:38 +08:00
|
|
|
warn(
|
2020-03-07 04:39:54 +08:00
|
|
|
`Hydration children mismatch in <${vnode.type as string}>: ` +
|
2020-03-04 05:12:38 +08:00
|
|
|
`server rendered element contains more child nodes than client vdom.`
|
|
|
|
)
|
2020-03-07 04:39:54 +08:00
|
|
|
hasWarned = true
|
|
|
|
}
|
2020-03-04 05:12:38 +08:00
|
|
|
// The SSRed DOM contains more nodes than it should. Remove them.
|
|
|
|
const cur = next
|
|
|
|
next = next.nextSibling
|
|
|
|
el.removeChild(cur)
|
|
|
|
}
|
|
|
|
} else if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
|
2020-03-07 04:39:54 +08:00
|
|
|
if (el.textContent !== vnode.children) {
|
|
|
|
hasMismatch = true
|
|
|
|
__DEV__ &&
|
|
|
|
warn(
|
|
|
|
`Hydration text content mismatch in <${vnode.type as string}>:\n` +
|
|
|
|
`- Client: ${el.textContent}\n` +
|
|
|
|
`- Server: ${vnode.children as string}`
|
|
|
|
)
|
|
|
|
el.textContent = vnode.children as string
|
|
|
|
}
|
2020-02-14 12:31:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return el.nextSibling
|
|
|
|
}
|
|
|
|
|
2020-02-14 16:22:52 +08:00
|
|
|
const hydrateChildren = (
|
2020-02-27 05:32:06 +08:00
|
|
|
node: Node | null,
|
2020-02-28 00:26:39 +08:00
|
|
|
vnode: VNode,
|
2020-03-04 05:12:38 +08:00
|
|
|
container: Element,
|
2020-02-28 00:26:39 +08:00
|
|
|
parentComponent: ComponentInternalInstance | null,
|
2020-03-13 10:19:41 +08:00
|
|
|
parentSuspense: SuspenseBoundary | null,
|
2020-02-28 00:26:39 +08:00
|
|
|
optimized: boolean
|
2020-02-27 05:32:06 +08:00
|
|
|
): Node | null => {
|
2020-02-28 00:26:39 +08:00
|
|
|
optimized = optimized || vnode.dynamicChildren !== null
|
2020-03-05 07:06:50 +08:00
|
|
|
const children = vnode.children as VNode[]
|
|
|
|
const l = children.length
|
2020-03-07 04:39:54 +08:00
|
|
|
let hasWarned = false
|
2020-03-05 07:06:50 +08:00
|
|
|
for (let i = 0; i < l; i++) {
|
2020-02-28 00:26:39 +08:00
|
|
|
const vnode = optimized
|
|
|
|
? children[i]
|
|
|
|
: (children[i] = normalizeVNode(children[i]))
|
2020-03-04 05:12:38 +08:00
|
|
|
if (node) {
|
2020-03-13 10:19:41 +08:00
|
|
|
node = hydrateNode(
|
|
|
|
node,
|
|
|
|
vnode,
|
|
|
|
parentComponent,
|
|
|
|
parentSuspense,
|
|
|
|
optimized
|
|
|
|
)
|
2020-03-04 05:12:38 +08:00
|
|
|
} else {
|
2020-03-05 07:06:50 +08:00
|
|
|
hasMismatch = true
|
2020-03-07 04:39:54 +08:00
|
|
|
if (__DEV__ && !hasWarned) {
|
2020-03-04 05:12:38 +08:00
|
|
|
warn(
|
2020-03-07 04:39:54 +08:00
|
|
|
`Hydration children mismatch in <${container.tagName.toLowerCase()}>: ` +
|
2020-03-04 05:12:38 +08:00
|
|
|
`server rendered element contains fewer child nodes than client vdom.`
|
|
|
|
)
|
2020-03-07 04:39:54 +08:00
|
|
|
hasWarned = true
|
|
|
|
}
|
2020-03-04 05:12:38 +08:00
|
|
|
// the SSRed DOM didn't contain enough nodes. Mount the missing ones.
|
2020-03-13 10:19:41 +08:00
|
|
|
patch(
|
|
|
|
null,
|
|
|
|
vnode,
|
|
|
|
container,
|
|
|
|
null,
|
|
|
|
parentComponent,
|
|
|
|
parentSuspense,
|
|
|
|
isSVGContainer(container)
|
|
|
|
)
|
2020-03-04 05:12:38 +08:00
|
|
|
}
|
2020-02-14 12:31:03 +08:00
|
|
|
}
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
|
2020-02-28 00:26:39 +08:00
|
|
|
const hydrateFragment = (
|
2020-03-13 10:19:41 +08:00
|
|
|
node: Comment,
|
2020-02-28 00:26:39 +08:00
|
|
|
vnode: VNode,
|
|
|
|
parentComponent: ComponentInternalInstance | null,
|
2020-03-13 10:19:41 +08:00
|
|
|
parentSuspense: SuspenseBoundary | null,
|
2020-02-28 00:26:39 +08:00
|
|
|
optimized: boolean
|
|
|
|
) => {
|
2020-03-13 10:19:41 +08:00
|
|
|
return nextSibling(
|
|
|
|
(vnode.anchor = hydrateChildren(
|
|
|
|
nextSibling(node)!,
|
|
|
|
vnode,
|
|
|
|
parentNode(node)!,
|
|
|
|
parentComponent,
|
|
|
|
parentSuspense,
|
|
|
|
optimized
|
|
|
|
)!)
|
2020-03-04 05:12:38 +08:00
|
|
|
)
|
2020-02-28 00:26:39 +08:00
|
|
|
}
|
|
|
|
|
2020-02-15 10:04:08 +08:00
|
|
|
const hydratePortal = (
|
|
|
|
vnode: VNode,
|
2020-02-28 00:26:39 +08:00
|
|
|
parentComponent: ComponentInternalInstance | null,
|
2020-03-13 10:19:41 +08:00
|
|
|
parentSuspense: SuspenseBoundary | null,
|
2020-02-28 00:26:39 +08:00
|
|
|
optimized: boolean
|
2020-02-15 10:04:08 +08:00
|
|
|
) => {
|
|
|
|
const targetSelector = vnode.props && vnode.props.target
|
|
|
|
const target = (vnode.target = isString(targetSelector)
|
|
|
|
? document.querySelector(targetSelector)
|
|
|
|
: targetSelector)
|
|
|
|
if (target != null && vnode.shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
|
2020-03-04 05:12:38 +08:00
|
|
|
hydrateChildren(
|
|
|
|
target.firstChild,
|
|
|
|
vnode,
|
2020-03-05 07:06:50 +08:00
|
|
|
target,
|
2020-03-04 05:12:38 +08:00
|
|
|
parentComponent,
|
2020-03-13 10:19:41 +08:00
|
|
|
parentSuspense,
|
2020-03-04 05:12:38 +08:00
|
|
|
optimized
|
|
|
|
)
|
2020-03-05 07:06:50 +08:00
|
|
|
} else if (__DEV__) {
|
|
|
|
warn(
|
|
|
|
`Attempting to hydrate portal but target ${targetSelector} does not ` +
|
|
|
|
`exist in server-rendered markup.`
|
|
|
|
)
|
2020-02-15 10:04:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 05:12:38 +08:00
|
|
|
const handleMismtach = (
|
|
|
|
node: Node,
|
|
|
|
vnode: VNode,
|
2020-03-13 10:19:41 +08:00
|
|
|
parentComponent: ComponentInternalInstance | null,
|
|
|
|
parentSuspense: SuspenseBoundary | null
|
2020-03-04 05:12:38 +08:00
|
|
|
) => {
|
2020-03-05 07:06:50 +08:00
|
|
|
hasMismatch = true
|
2020-03-04 05:12:38 +08:00
|
|
|
__DEV__ &&
|
|
|
|
warn(
|
|
|
|
`Hydration node mismatch:\n- Client vnode:`,
|
|
|
|
vnode.type,
|
|
|
|
`\n- Server rendered DOM:`,
|
2020-03-13 10:19:41 +08:00
|
|
|
node,
|
|
|
|
node.nodeType === DOMNodeTypes.TEXT ? `(text)` : ``
|
2020-03-04 05:12:38 +08:00
|
|
|
)
|
|
|
|
vnode.el = null
|
2020-03-13 10:19:41 +08:00
|
|
|
const next = nextSibling(node)
|
|
|
|
const container = parentNode(node)!
|
2020-03-04 05:12:38 +08:00
|
|
|
container.removeChild(node)
|
2020-03-13 10:19:41 +08:00
|
|
|
// TODO Suspense
|
|
|
|
patch(
|
|
|
|
null,
|
|
|
|
vnode,
|
|
|
|
container,
|
|
|
|
next,
|
|
|
|
parentComponent,
|
|
|
|
parentSuspense,
|
|
|
|
isSVGContainer(container)
|
|
|
|
)
|
2020-03-04 05:12:38 +08:00
|
|
|
return next
|
|
|
|
}
|
|
|
|
|
2020-03-13 10:19:41 +08:00
|
|
|
const locateClosingAsyncAnchor = (node: Node | null): Node | null => {
|
|
|
|
let match = 0
|
|
|
|
while (node) {
|
|
|
|
node = nextSibling(node)
|
|
|
|
if (node && isComment(node)) {
|
|
|
|
if (node.data === '1') match++
|
|
|
|
if (node.data === '0') {
|
|
|
|
if (match === 0) {
|
|
|
|
return nextSibling(node)
|
|
|
|
} else {
|
|
|
|
match--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
|
2020-02-14 12:31:03 +08:00
|
|
|
return [hydrate, hydrateNode] as const
|
|
|
|
}
|