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,
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
// 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,
} 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 = (
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isFragment: boolean
- ) => {
+ ): Node | null => {
hasMismatch = true
__DEV__ &&
warn(
import { updateProps } from './componentProps'
import { updateSlots } from './componentSlots'
import { pushWarningContext, popWarningContext, warn } from './warning'
-import { ComponentPublicInstance } from './componentProxy'
import { createAppAPI, CreateAppFunction } from './apiCreateApp'
import {
SuspenseBoundary,
? queueEffectWithSuspense
: queuePostFlushCb
+export const setRef = (
+ rawRef: VNodeNormalizedRef,
+ oldRawRef: VNodeNormalizedRef | null,
+ parent: ComponentInternalInstance,
+ vnode: VNode | null
+) => {
+ const value = vnode
+ ? vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
+ ? vnode.component!.proxy
+ : vnode.el
+ : null
+ const [owner, ref] = rawRef
+ if (__DEV__ && !owner) {
+ warn(
+ `Missing ref owner context. ref cannot be used on hoisted vnodes. ` +
+ `A vnode with ref must be created inside the render function.`
+ )
+ return
+ }
+ const oldRef = oldRawRef && oldRawRef[1]
+ const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
+ const setupState = owner.setupState
+
+ // unset old ref
+ if (oldRef != null && oldRef !== ref) {
+ if (isString(oldRef)) {
+ refs[oldRef] = null
+ if (hasOwn(setupState, oldRef)) {
+ setupState[oldRef] = null
+ }
+ } else if (isRef(oldRef)) {
+ oldRef.value = null
+ }
+ }
+
+ if (isString(ref)) {
+ refs[ref] = value
+ if (hasOwn(setupState, ref)) {
+ setupState[ref] = value
+ }
+ } else if (isRef(ref)) {
+ ref.value = value
+ } else if (isFunction(ref)) {
+ callWithErrorHandling(ref, parent, ErrorCodes.FUNCTION_REF, [value, refs])
+ } else if (__DEV__) {
+ warn('Invalid template ref type:', value, `(${typeof value})`)
+ }
+}
+
/**
* The createRenderer function accepts two generic arguments:
* HostNode and HostElement, corresponding to Node and Element types in the
// set ref
if (ref != null && parentComponent) {
- const refValue =
- shapeFlag & ShapeFlags.STATEFUL_COMPONENT ? n2.component!.proxy : n2.el
- setRef(ref, n1 && n1.ref, parentComponent, refValue)
+ setRef(ref, n1 && n1.ref, parentComponent, n2)
}
}
return hostNextSibling((vnode.anchor || vnode.el)!)
}
- const setRef = (
- rawRef: VNodeNormalizedRef,
- oldRawRef: VNodeNormalizedRef | null,
- parent: ComponentInternalInstance,
- value: RendererNode | ComponentPublicInstance | null
- ) => {
- const [owner, ref] = rawRef
- if (__DEV__ && !owner) {
- warn(
- `Missing ref owner context. ref cannot be used on hoisted vnodes. ` +
- `A vnode with ref must be created inside the render function.`
- )
- return
- }
- const oldRef = oldRawRef && oldRawRef[1]
- const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
- const setupState = owner.setupState
-
- // unset old ref
- if (oldRef != null && oldRef !== ref) {
- if (isString(oldRef)) {
- refs[oldRef] = null
- if (hasOwn(setupState, oldRef)) {
- setupState[oldRef] = null
- }
- } else if (isRef(oldRef)) {
- oldRef.value = null
- }
- }
-
- if (isString(ref)) {
- refs[ref] = value
- if (hasOwn(setupState, ref)) {
- setupState[ref] = value
- }
- } else if (isRef(ref)) {
- ref.value = value
- } else if (isFunction(ref)) {
- callWithErrorHandling(ref, parent, ErrorCodes.FUNCTION_REF, [value, refs])
- } else if (__DEV__) {
- warn('Invalid template ref type:', value, `(${typeof value})`)
- }
- }
-
/**
* #1156
* When a component is HMR-enabled, we need to make sure that all static nodes