|
|
@@ -12,7 +12,7 @@ import { ComponentOptions, ComponentInternalInstance } from './component'
|
|
|
import { invokeDirectiveHook } from './directives'
|
|
|
import { warn } from './warning'
|
|
|
import { PatchFlags, ShapeFlags, isReservedProp, isOn } from '@vue/shared'
|
|
|
-import { RendererInternals, invokeVNodeHook } from './renderer'
|
|
|
+import { RendererInternals, invokeVNodeHook, setRef } from './renderer'
|
|
|
import {
|
|
|
SuspenseImpl,
|
|
|
SuspenseBoundary,
|
|
|
@@ -88,74 +88,85 @@ export function createHydrationFunctions(
|
|
|
isFragmentStart
|
|
|
)
|
|
|
|
|
|
- const { type, shapeFlag } = vnode
|
|
|
+ const { type, ref, shapeFlag } = vnode
|
|
|
const domType = node.nodeType
|
|
|
vnode.el = node
|
|
|
|
|
|
+ let nextNode: Node | null = null
|
|
|
switch (type) {
|
|
|
case Text:
|
|
|
if (domType !== DOMNodeTypes.TEXT) {
|
|
|
- return onMismatch()
|
|
|
- }
|
|
|
- if ((node as Text).data !== vnode.children) {
|
|
|
- hasMismatch = true
|
|
|
- __DEV__ &&
|
|
|
- warn(
|
|
|
- `Hydration text mismatch:` +
|
|
|
- `\n- Client: ${JSON.stringify((node as Text).data)}` +
|
|
|
- `\n- Server: ${JSON.stringify(vnode.children)}`
|
|
|
- )
|
|
|
- ;(node as Text).data = vnode.children as string
|
|
|
+ nextNode = onMismatch()
|
|
|
+ } else {
|
|
|
+ if ((node as Text).data !== vnode.children) {
|
|
|
+ hasMismatch = true
|
|
|
+ __DEV__ &&
|
|
|
+ warn(
|
|
|
+ `Hydration text mismatch:` +
|
|
|
+ `\n- Client: ${JSON.stringify((node as Text).data)}` +
|
|
|
+ `\n- Server: ${JSON.stringify(vnode.children)}`
|
|
|
+ )
|
|
|
+ ;(node as Text).data = vnode.children as string
|
|
|
+ }
|
|
|
+ nextNode = nextSibling(node)
|
|
|
}
|
|
|
- return nextSibling(node)
|
|
|
+ break
|
|
|
case Comment:
|
|
|
if (domType !== DOMNodeTypes.COMMENT || isFragmentStart) {
|
|
|
- return onMismatch()
|
|
|
+ nextNode = onMismatch()
|
|
|
+ } else {
|
|
|
+ nextNode = nextSibling(node)
|
|
|
}
|
|
|
- return nextSibling(node)
|
|
|
+ break
|
|
|
case Static:
|
|
|
if (domType !== DOMNodeTypes.ELEMENT) {
|
|
|
- return onMismatch()
|
|
|
- }
|
|
|
- // determine anchor, adopt content
|
|
|
- let cur = node
|
|
|
- // if the static vnode has its content stripped during build,
|
|
|
- // adopt it from the server-rendered HTML.
|
|
|
- const needToAdoptContent = !(vnode.children as string).length
|
|
|
- for (let i = 0; i < vnode.staticCount; i++) {
|
|
|
- if (needToAdoptContent) vnode.children += (cur as Element).outerHTML
|
|
|
- if (i === vnode.staticCount - 1) {
|
|
|
- vnode.anchor = cur
|
|
|
+ nextNode = onMismatch()
|
|
|
+ } else {
|
|
|
+ // determine anchor, adopt content
|
|
|
+ nextNode = node
|
|
|
+ // if the static vnode has its content stripped during build,
|
|
|
+ // adopt it from the server-rendered HTML.
|
|
|
+ const needToAdoptContent = !(vnode.children as string).length
|
|
|
+ for (let i = 0; i < vnode.staticCount; i++) {
|
|
|
+ if (needToAdoptContent)
|
|
|
+ vnode.children += (nextNode as Element).outerHTML
|
|
|
+ if (i === vnode.staticCount - 1) {
|
|
|
+ vnode.anchor = nextNode
|
|
|
+ }
|
|
|
+ nextNode = nextSibling(nextNode)!
|
|
|
}
|
|
|
- cur = nextSibling(cur)!
|
|
|
+ return nextNode
|
|
|
}
|
|
|
- return cur
|
|
|
+ break
|
|
|
case Fragment:
|
|
|
if (!isFragmentStart) {
|
|
|
- return onMismatch()
|
|
|
+ nextNode = onMismatch()
|
|
|
+ } else {
|
|
|
+ nextNode = hydrateFragment(
|
|
|
+ node as Comment,
|
|
|
+ vnode,
|
|
|
+ parentComponent,
|
|
|
+ parentSuspense,
|
|
|
+ optimized
|
|
|
+ )
|
|
|
}
|
|
|
- return hydrateFragment(
|
|
|
- node as Comment,
|
|
|
- vnode,
|
|
|
- parentComponent,
|
|
|
- parentSuspense,
|
|
|
- optimized
|
|
|
- )
|
|
|
+ break
|
|
|
default:
|
|
|
if (shapeFlag & ShapeFlags.ELEMENT) {
|
|
|
if (
|
|
|
domType !== DOMNodeTypes.ELEMENT ||
|
|
|
vnode.type !== (node as Element).tagName.toLowerCase()
|
|
|
) {
|
|
|
- return onMismatch()
|
|
|
+ nextNode = onMismatch()
|
|
|
+ } else {
|
|
|
+ nextNode = hydrateElement(
|
|
|
+ node as Element,
|
|
|
+ vnode,
|
|
|
+ parentComponent,
|
|
|
+ parentSuspense,
|
|
|
+ optimized
|
|
|
+ )
|
|
|
}
|
|
|
- return hydrateElement(
|
|
|
- node as Element,
|
|
|
- vnode,
|
|
|
- parentComponent,
|
|
|
- parentSuspense,
|
|
|
- optimized
|
|
|
- )
|
|
|
} else if (shapeFlag & ShapeFlags.COMPONENT) {
|
|
|
// when setting up the render effect, if the initial vnode already
|
|
|
// has .el set, the component will perform hydration instead of mount
|
|
|
@@ -182,24 +193,25 @@ export function createHydrationFunctions(
|
|
|
// component may be async, so in the case of fragments we cannot rely
|
|
|
// on component's rendered output to determine the end of the fragment
|
|
|
// instead, we do a lookahead to find the end anchor node.
|
|
|
- return isFragmentStart
|
|
|
+ nextNode = isFragmentStart
|
|
|
? locateClosingAsyncAnchor(node)
|
|
|
: nextSibling(node)
|
|
|
} else if (shapeFlag & ShapeFlags.TELEPORT) {
|
|
|
if (domType !== DOMNodeTypes.COMMENT) {
|
|
|
- return onMismatch()
|
|
|
+ nextNode = onMismatch()
|
|
|
+ } else {
|
|
|
+ nextNode = (vnode.type as typeof TeleportImpl).hydrate(
|
|
|
+ node,
|
|
|
+ vnode,
|
|
|
+ parentComponent,
|
|
|
+ parentSuspense,
|
|
|
+ optimized,
|
|
|
+ rendererInternals,
|
|
|
+ hydrateChildren
|
|
|
+ )
|
|
|
}
|
|
|
- return (vnode.type as typeof TeleportImpl).hydrate(
|
|
|
- node,
|
|
|
- vnode,
|
|
|
- parentComponent,
|
|
|
- parentSuspense,
|
|
|
- optimized,
|
|
|
- rendererInternals,
|
|
|
- hydrateChildren
|
|
|
- )
|
|
|
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
|
|
|
- return (vnode.type as typeof SuspenseImpl).hydrate(
|
|
|
+ nextNode = (vnode.type as typeof SuspenseImpl).hydrate(
|
|
|
node,
|
|
|
vnode,
|
|
|
parentComponent,
|
|
|
@@ -212,8 +224,13 @@ export function createHydrationFunctions(
|
|
|
} else if (__DEV__) {
|
|
|
warn('Invalid HostVNode type:', type, `(${typeof type})`)
|
|
|
}
|
|
|
- return null
|
|
|
}
|
|
|
+
|
|
|
+ if (ref != null && parentComponent) {
|
|
|
+ setRef(ref, null, parentComponent, vnode)
|
|
|
+ }
|
|
|
+
|
|
|
+ return nextNode
|
|
|
}
|
|
|
|
|
|
const hydrateElement = (
|
|
|
@@ -386,7 +403,7 @@ export function createHydrationFunctions(
|
|
|
parentComponent: ComponentInternalInstance | null,
|
|
|
parentSuspense: SuspenseBoundary | null,
|
|
|
isFragment: boolean
|
|
|
- ) => {
|
|
|
+ ): Node | null => {
|
|
|
hasMismatch = true
|
|
|
__DEV__ &&
|
|
|
warn(
|