refactor: move template ref setter into dedicated file
This commit is contained in:
parent
41c18effea
commit
1928c9b537
@ -8,7 +8,13 @@ import {
|
|||||||
getComponentName,
|
getComponentName,
|
||||||
ComponentOptions
|
ComponentOptions
|
||||||
} from '../component'
|
} from '../component'
|
||||||
import { VNode, cloneVNode, isVNode, VNodeProps } from '../vnode'
|
import {
|
||||||
|
VNode,
|
||||||
|
cloneVNode,
|
||||||
|
isVNode,
|
||||||
|
VNodeProps,
|
||||||
|
invokeVNodeHook
|
||||||
|
} from '../vnode'
|
||||||
import { warn } from '../warning'
|
import { warn } from '../warning'
|
||||||
import {
|
import {
|
||||||
onBeforeUnmount,
|
onBeforeUnmount,
|
||||||
@ -30,8 +36,7 @@ import {
|
|||||||
queuePostRenderEffect,
|
queuePostRenderEffect,
|
||||||
MoveType,
|
MoveType,
|
||||||
RendererElement,
|
RendererElement,
|
||||||
RendererNode,
|
RendererNode
|
||||||
invokeVNodeHook
|
|
||||||
} from '../renderer'
|
} from '../renderer'
|
||||||
import { setTransitionHooks } from './BaseTransition'
|
import { setTransitionHooks } from './BaseTransition'
|
||||||
import { ComponentRenderContext } from '../componentPublicInstance'
|
import { ComponentRenderContext } from '../componentPublicInstance'
|
||||||
|
@ -7,14 +7,16 @@ import {
|
|||||||
Fragment,
|
Fragment,
|
||||||
VNodeHook,
|
VNodeHook,
|
||||||
createVNode,
|
createVNode,
|
||||||
createTextVNode
|
createTextVNode,
|
||||||
|
invokeVNodeHook
|
||||||
} from './vnode'
|
} from './vnode'
|
||||||
import { flushPostFlushCbs } from './scheduler'
|
import { flushPostFlushCbs } from './scheduler'
|
||||||
import { ComponentInternalInstance } from './component'
|
import { ComponentInternalInstance } from './component'
|
||||||
import { invokeDirectiveHook } from './directives'
|
import { invokeDirectiveHook } from './directives'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { PatchFlags, ShapeFlags, isReservedProp, isOn } from '@vue/shared'
|
import { PatchFlags, ShapeFlags, isReservedProp, isOn } from '@vue/shared'
|
||||||
import { RendererInternals, invokeVNodeHook, setRef } from './renderer'
|
import { RendererInternals } from './renderer'
|
||||||
|
import { setRef } from './rendererTemplateRef'
|
||||||
import {
|
import {
|
||||||
SuspenseImpl,
|
SuspenseImpl,
|
||||||
SuspenseBoundary,
|
SuspenseBoundary,
|
||||||
|
@ -9,17 +9,15 @@ import {
|
|||||||
createVNode,
|
createVNode,
|
||||||
isSameVNodeType,
|
isSameVNodeType,
|
||||||
Static,
|
Static,
|
||||||
VNodeNormalizedRef,
|
|
||||||
VNodeHook,
|
VNodeHook,
|
||||||
VNodeNormalizedRefAtom,
|
VNodeProps,
|
||||||
VNodeProps
|
invokeVNodeHook
|
||||||
} from './vnode'
|
} from './vnode'
|
||||||
import {
|
import {
|
||||||
ComponentInternalInstance,
|
ComponentInternalInstance,
|
||||||
ComponentOptions,
|
ComponentOptions,
|
||||||
createComponentInstance,
|
createComponentInstance,
|
||||||
Data,
|
Data,
|
||||||
getExposeProxy,
|
|
||||||
setupComponent
|
setupComponent
|
||||||
} from './component'
|
} from './component'
|
||||||
import {
|
import {
|
||||||
@ -29,19 +27,15 @@ import {
|
|||||||
updateHOCHostEl
|
updateHOCHostEl
|
||||||
} from './componentRenderUtils'
|
} from './componentRenderUtils'
|
||||||
import {
|
import {
|
||||||
isString,
|
|
||||||
EMPTY_OBJ,
|
EMPTY_OBJ,
|
||||||
EMPTY_ARR,
|
EMPTY_ARR,
|
||||||
isReservedProp,
|
isReservedProp,
|
||||||
isFunction,
|
|
||||||
PatchFlags,
|
PatchFlags,
|
||||||
ShapeFlags,
|
ShapeFlags,
|
||||||
NOOP,
|
NOOP,
|
||||||
hasOwn,
|
|
||||||
invokeArrayFns,
|
invokeArrayFns,
|
||||||
isArray,
|
isArray,
|
||||||
getGlobalThis,
|
getGlobalThis
|
||||||
remove
|
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
queueJob,
|
queueJob,
|
||||||
@ -51,16 +45,12 @@ import {
|
|||||||
flushPreFlushCbs,
|
flushPreFlushCbs,
|
||||||
SchedulerJob
|
SchedulerJob
|
||||||
} from './scheduler'
|
} from './scheduler'
|
||||||
import {
|
import { pauseTracking, resetTracking, ReactiveEffect } from '@vue/reactivity'
|
||||||
isRef,
|
|
||||||
pauseTracking,
|
|
||||||
resetTracking,
|
|
||||||
ReactiveEffect
|
|
||||||
} from '@vue/reactivity'
|
|
||||||
import { updateProps } from './componentProps'
|
import { updateProps } from './componentProps'
|
||||||
import { updateSlots } from './componentSlots'
|
import { updateSlots } from './componentSlots'
|
||||||
import { pushWarningContext, popWarningContext, warn } from './warning'
|
import { pushWarningContext, popWarningContext, warn } from './warning'
|
||||||
import { createAppAPI, CreateAppFunction } from './apiCreateApp'
|
import { createAppAPI, CreateAppFunction } from './apiCreateApp'
|
||||||
|
import { setRef } from './rendererTemplateRef'
|
||||||
import {
|
import {
|
||||||
SuspenseBoundary,
|
SuspenseBoundary,
|
||||||
queueEffectWithSuspense,
|
queueEffectWithSuspense,
|
||||||
@ -69,11 +59,6 @@ import {
|
|||||||
import { TeleportImpl, TeleportVNode } from './components/Teleport'
|
import { TeleportImpl, TeleportVNode } from './components/Teleport'
|
||||||
import { isKeepAlive, KeepAliveContext } from './components/KeepAlive'
|
import { isKeepAlive, KeepAliveContext } from './components/KeepAlive'
|
||||||
import { registerHMR, unregisterHMR, isHmrUpdating } from './hmr'
|
import { registerHMR, unregisterHMR, isHmrUpdating } from './hmr'
|
||||||
import {
|
|
||||||
ErrorCodes,
|
|
||||||
callWithErrorHandling,
|
|
||||||
callWithAsyncErrorHandling
|
|
||||||
} from './errorHandling'
|
|
||||||
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
|
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
|
||||||
import { invokeDirectiveHook } from './directives'
|
import { invokeDirectiveHook } from './directives'
|
||||||
import { startMeasure, endMeasure } from './profiling'
|
import { startMeasure, endMeasure } from './profiling'
|
||||||
@ -2351,124 +2336,6 @@ function baseCreateRenderer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setRef(
|
|
||||||
rawRef: VNodeNormalizedRef,
|
|
||||||
oldRawRef: VNodeNormalizedRef | null,
|
|
||||||
parentSuspense: SuspenseBoundary | null,
|
|
||||||
vnode: VNode,
|
|
||||||
isUnmount = false
|
|
||||||
) {
|
|
||||||
if (isArray(rawRef)) {
|
|
||||||
rawRef.forEach((r, i) =>
|
|
||||||
setRef(
|
|
||||||
r,
|
|
||||||
oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef),
|
|
||||||
parentSuspense,
|
|
||||||
vnode,
|
|
||||||
isUnmount
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAsyncWrapper(vnode) && !isUnmount) {
|
|
||||||
// when mounting async components, nothing needs to be done,
|
|
||||||
// because the template ref is forwarded to inner component
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const refValue =
|
|
||||||
vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
|
|
||||||
? getExposeProxy(vnode.component!) || vnode.component!.proxy
|
|
||||||
: vnode.el
|
|
||||||
const value = isUnmount ? null : refValue
|
|
||||||
|
|
||||||
const { i: owner, r: 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 as VNodeNormalizedRefAtom).r
|
|
||||||
const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
|
|
||||||
const setupState = owner.setupState
|
|
||||||
|
|
||||||
// dynamic ref changed. 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 (isFunction(ref)) {
|
|
||||||
callWithErrorHandling(ref, owner, ErrorCodes.FUNCTION_REF, [value, refs])
|
|
||||||
} else {
|
|
||||||
const _isString = isString(ref)
|
|
||||||
const _isRef = isRef(ref)
|
|
||||||
if (_isString || _isRef) {
|
|
||||||
const doSet = () => {
|
|
||||||
if (rawRef.f) {
|
|
||||||
const existing = _isString ? refs[ref] : ref.value
|
|
||||||
if (isUnmount) {
|
|
||||||
isArray(existing) && remove(existing, refValue)
|
|
||||||
} else {
|
|
||||||
if (!isArray(existing)) {
|
|
||||||
if (_isString) {
|
|
||||||
refs[ref] = [refValue]
|
|
||||||
} else {
|
|
||||||
ref.value = [refValue]
|
|
||||||
if (rawRef.k) refs[rawRef.k] = ref.value
|
|
||||||
}
|
|
||||||
} else if (!existing.includes(refValue)) {
|
|
||||||
existing.push(refValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (_isString) {
|
|
||||||
refs[ref] = value
|
|
||||||
if (hasOwn(setupState, ref)) {
|
|
||||||
setupState[ref] = value
|
|
||||||
}
|
|
||||||
} else if (isRef(ref)) {
|
|
||||||
ref.value = value
|
|
||||||
if (rawRef.k) refs[rawRef.k] = value
|
|
||||||
} else if (__DEV__) {
|
|
||||||
warn('Invalid template ref type:', ref, `(${typeof ref})`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (value) {
|
|
||||||
// #1789: for non-null values, set them after render
|
|
||||||
// null values means this is unmount and it should not overwrite another
|
|
||||||
// ref with the same key
|
|
||||||
;(doSet as SchedulerJob).id = -1
|
|
||||||
queuePostRenderEffect(doSet, parentSuspense)
|
|
||||||
} else {
|
|
||||||
doSet()
|
|
||||||
}
|
|
||||||
} else if (__DEV__) {
|
|
||||||
warn('Invalid template ref type:', ref, `(${typeof ref})`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function invokeVNodeHook(
|
|
||||||
hook: VNodeHook,
|
|
||||||
instance: ComponentInternalInstance | null,
|
|
||||||
vnode: VNode,
|
|
||||||
prevVNode: VNode | null = null
|
|
||||||
) {
|
|
||||||
callWithAsyncErrorHandling(hook, instance, ErrorCodes.VNODE_HOOK, [
|
|
||||||
vnode,
|
|
||||||
prevVNode
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleRecurse(
|
function toggleRecurse(
|
||||||
{ effect, update }: ComponentInternalInstance,
|
{ effect, update }: ComponentInternalInstance,
|
||||||
allowed: boolean
|
allowed: boolean
|
||||||
|
127
packages/runtime-core/src/rendererTemplateRef.ts
Normal file
127
packages/runtime-core/src/rendererTemplateRef.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import { SuspenseBoundary } from './components/Suspense'
|
||||||
|
import { VNode, VNodeNormalizedRef, VNodeNormalizedRefAtom } from './vnode'
|
||||||
|
import {
|
||||||
|
EMPTY_OBJ,
|
||||||
|
hasOwn,
|
||||||
|
isArray,
|
||||||
|
isFunction,
|
||||||
|
isString,
|
||||||
|
remove,
|
||||||
|
ShapeFlags
|
||||||
|
} from '@vue/shared'
|
||||||
|
import { isAsyncWrapper } from './apiAsyncComponent'
|
||||||
|
import { getExposeProxy } from './component'
|
||||||
|
import { warn } from './warning'
|
||||||
|
import { isRef } from '@vue/reactivity'
|
||||||
|
import { callWithErrorHandling, ErrorCodes } from './errorHandling'
|
||||||
|
import { SchedulerJob } from './scheduler'
|
||||||
|
import { queuePostRenderEffect } from './renderer'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function for handling a template ref
|
||||||
|
*/
|
||||||
|
export function setRef(
|
||||||
|
rawRef: VNodeNormalizedRef,
|
||||||
|
oldRawRef: VNodeNormalizedRef | null,
|
||||||
|
parentSuspense: SuspenseBoundary | null,
|
||||||
|
vnode: VNode,
|
||||||
|
isUnmount = false
|
||||||
|
) {
|
||||||
|
if (isArray(rawRef)) {
|
||||||
|
rawRef.forEach((r, i) =>
|
||||||
|
setRef(
|
||||||
|
r,
|
||||||
|
oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef),
|
||||||
|
parentSuspense,
|
||||||
|
vnode,
|
||||||
|
isUnmount
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAsyncWrapper(vnode) && !isUnmount) {
|
||||||
|
// when mounting async components, nothing needs to be done,
|
||||||
|
// because the template ref is forwarded to inner component
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const refValue =
|
||||||
|
vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
|
||||||
|
? getExposeProxy(vnode.component!) || vnode.component!.proxy
|
||||||
|
: vnode.el
|
||||||
|
const value = isUnmount ? null : refValue
|
||||||
|
|
||||||
|
const { i: owner, r: 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 as VNodeNormalizedRefAtom).r
|
||||||
|
const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
|
||||||
|
const setupState = owner.setupState
|
||||||
|
|
||||||
|
// dynamic ref changed. 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 (isFunction(ref)) {
|
||||||
|
callWithErrorHandling(ref, owner, ErrorCodes.FUNCTION_REF, [value, refs])
|
||||||
|
} else {
|
||||||
|
const _isString = isString(ref)
|
||||||
|
const _isRef = isRef(ref)
|
||||||
|
if (_isString || _isRef) {
|
||||||
|
const doSet = () => {
|
||||||
|
if (rawRef.f) {
|
||||||
|
const existing = _isString ? refs[ref] : ref.value
|
||||||
|
if (isUnmount) {
|
||||||
|
isArray(existing) && remove(existing, refValue)
|
||||||
|
} else {
|
||||||
|
if (!isArray(existing)) {
|
||||||
|
if (_isString) {
|
||||||
|
refs[ref] = [refValue]
|
||||||
|
} else {
|
||||||
|
ref.value = [refValue]
|
||||||
|
if (rawRef.k) refs[rawRef.k] = ref.value
|
||||||
|
}
|
||||||
|
} else if (!existing.includes(refValue)) {
|
||||||
|
existing.push(refValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (_isString) {
|
||||||
|
refs[ref] = value
|
||||||
|
if (hasOwn(setupState, ref)) {
|
||||||
|
setupState[ref] = value
|
||||||
|
}
|
||||||
|
} else if (isRef(ref)) {
|
||||||
|
ref.value = value
|
||||||
|
if (rawRef.k) refs[rawRef.k] = value
|
||||||
|
} else if (__DEV__) {
|
||||||
|
warn('Invalid template ref type:', ref, `(${typeof ref})`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value) {
|
||||||
|
// #1789: for non-null values, set them after render
|
||||||
|
// null values means this is unmount and it should not overwrite another
|
||||||
|
// ref with the same key
|
||||||
|
;(doSet as SchedulerJob).id = -1
|
||||||
|
queuePostRenderEffect(doSet, parentSuspense)
|
||||||
|
} else {
|
||||||
|
doSet()
|
||||||
|
}
|
||||||
|
} else if (__DEV__) {
|
||||||
|
warn('Invalid template ref type:', ref, `(${typeof ref})`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -42,6 +42,7 @@ import { hmrDirtyComponents } from './hmr'
|
|||||||
import { convertLegacyComponent } from './compat/component'
|
import { convertLegacyComponent } from './compat/component'
|
||||||
import { convertLegacyVModelProps } from './compat/componentVModel'
|
import { convertLegacyVModelProps } from './compat/componentVModel'
|
||||||
import { defineLegacyVNodeProperties } from './compat/renderFn'
|
import { defineLegacyVNodeProperties } from './compat/renderFn'
|
||||||
|
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
|
||||||
|
|
||||||
export const Fragment = Symbol(__DEV__ ? 'Fragment' : undefined) as any as {
|
export const Fragment = Symbol(__DEV__ ? 'Fragment' : undefined) as any as {
|
||||||
__isFragment: true
|
__isFragment: true
|
||||||
@ -811,3 +812,15 @@ export function mergeProps(...args: (Data & VNodeProps)[]) {
|
|||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function invokeVNodeHook(
|
||||||
|
hook: VNodeHook,
|
||||||
|
instance: ComponentInternalInstance | null,
|
||||||
|
vnode: VNode,
|
||||||
|
prevVNode: VNode | null = null
|
||||||
|
) {
|
||||||
|
callWithAsyncErrorHandling(hook, instance, ErrorCodes.VNODE_HOOK, [
|
||||||
|
vnode,
|
||||||
|
prevVNode
|
||||||
|
])
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user