refactor: move template ref setter into dedicated file
This commit is contained in:
		
							parent
							
								
									41c18effea
								
							
						
					
					
						commit
						1928c9b537
					
				@ -8,7 +8,13 @@ import {
 | 
			
		||||
  getComponentName,
 | 
			
		||||
  ComponentOptions
 | 
			
		||||
} from '../component'
 | 
			
		||||
import { VNode, cloneVNode, isVNode, VNodeProps } from '../vnode'
 | 
			
		||||
import {
 | 
			
		||||
  VNode,
 | 
			
		||||
  cloneVNode,
 | 
			
		||||
  isVNode,
 | 
			
		||||
  VNodeProps,
 | 
			
		||||
  invokeVNodeHook
 | 
			
		||||
} from '../vnode'
 | 
			
		||||
import { warn } from '../warning'
 | 
			
		||||
import {
 | 
			
		||||
  onBeforeUnmount,
 | 
			
		||||
@ -30,8 +36,7 @@ import {
 | 
			
		||||
  queuePostRenderEffect,
 | 
			
		||||
  MoveType,
 | 
			
		||||
  RendererElement,
 | 
			
		||||
  RendererNode,
 | 
			
		||||
  invokeVNodeHook
 | 
			
		||||
  RendererNode
 | 
			
		||||
} from '../renderer'
 | 
			
		||||
import { setTransitionHooks } from './BaseTransition'
 | 
			
		||||
import { ComponentRenderContext } from '../componentPublicInstance'
 | 
			
		||||
 | 
			
		||||
@ -7,14 +7,16 @@ import {
 | 
			
		||||
  Fragment,
 | 
			
		||||
  VNodeHook,
 | 
			
		||||
  createVNode,
 | 
			
		||||
  createTextVNode
 | 
			
		||||
  createTextVNode,
 | 
			
		||||
  invokeVNodeHook
 | 
			
		||||
} from './vnode'
 | 
			
		||||
import { flushPostFlushCbs } from './scheduler'
 | 
			
		||||
import { ComponentInternalInstance } from './component'
 | 
			
		||||
import { invokeDirectiveHook } from './directives'
 | 
			
		||||
import { warn } from './warning'
 | 
			
		||||
import { PatchFlags, ShapeFlags, isReservedProp, isOn } from '@vue/shared'
 | 
			
		||||
import { RendererInternals, invokeVNodeHook, setRef } from './renderer'
 | 
			
		||||
import { RendererInternals } from './renderer'
 | 
			
		||||
import { setRef } from './rendererTemplateRef'
 | 
			
		||||
import {
 | 
			
		||||
  SuspenseImpl,
 | 
			
		||||
  SuspenseBoundary,
 | 
			
		||||
 | 
			
		||||
@ -9,17 +9,15 @@ import {
 | 
			
		||||
  createVNode,
 | 
			
		||||
  isSameVNodeType,
 | 
			
		||||
  Static,
 | 
			
		||||
  VNodeNormalizedRef,
 | 
			
		||||
  VNodeHook,
 | 
			
		||||
  VNodeNormalizedRefAtom,
 | 
			
		||||
  VNodeProps
 | 
			
		||||
  VNodeProps,
 | 
			
		||||
  invokeVNodeHook
 | 
			
		||||
} from './vnode'
 | 
			
		||||
import {
 | 
			
		||||
  ComponentInternalInstance,
 | 
			
		||||
  ComponentOptions,
 | 
			
		||||
  createComponentInstance,
 | 
			
		||||
  Data,
 | 
			
		||||
  getExposeProxy,
 | 
			
		||||
  setupComponent
 | 
			
		||||
} from './component'
 | 
			
		||||
import {
 | 
			
		||||
@ -29,19 +27,15 @@ import {
 | 
			
		||||
  updateHOCHostEl
 | 
			
		||||
} from './componentRenderUtils'
 | 
			
		||||
import {
 | 
			
		||||
  isString,
 | 
			
		||||
  EMPTY_OBJ,
 | 
			
		||||
  EMPTY_ARR,
 | 
			
		||||
  isReservedProp,
 | 
			
		||||
  isFunction,
 | 
			
		||||
  PatchFlags,
 | 
			
		||||
  ShapeFlags,
 | 
			
		||||
  NOOP,
 | 
			
		||||
  hasOwn,
 | 
			
		||||
  invokeArrayFns,
 | 
			
		||||
  isArray,
 | 
			
		||||
  getGlobalThis,
 | 
			
		||||
  remove
 | 
			
		||||
  getGlobalThis
 | 
			
		||||
} from '@vue/shared'
 | 
			
		||||
import {
 | 
			
		||||
  queueJob,
 | 
			
		||||
@ -51,16 +45,12 @@ import {
 | 
			
		||||
  flushPreFlushCbs,
 | 
			
		||||
  SchedulerJob
 | 
			
		||||
} from './scheduler'
 | 
			
		||||
import {
 | 
			
		||||
  isRef,
 | 
			
		||||
  pauseTracking,
 | 
			
		||||
  resetTracking,
 | 
			
		||||
  ReactiveEffect
 | 
			
		||||
} from '@vue/reactivity'
 | 
			
		||||
import { pauseTracking, resetTracking, ReactiveEffect } from '@vue/reactivity'
 | 
			
		||||
import { updateProps } from './componentProps'
 | 
			
		||||
import { updateSlots } from './componentSlots'
 | 
			
		||||
import { pushWarningContext, popWarningContext, warn } from './warning'
 | 
			
		||||
import { createAppAPI, CreateAppFunction } from './apiCreateApp'
 | 
			
		||||
import { setRef } from './rendererTemplateRef'
 | 
			
		||||
import {
 | 
			
		||||
  SuspenseBoundary,
 | 
			
		||||
  queueEffectWithSuspense,
 | 
			
		||||
@ -69,11 +59,6 @@ import {
 | 
			
		||||
import { TeleportImpl, TeleportVNode } from './components/Teleport'
 | 
			
		||||
import { isKeepAlive, KeepAliveContext } from './components/KeepAlive'
 | 
			
		||||
import { registerHMR, unregisterHMR, isHmrUpdating } from './hmr'
 | 
			
		||||
import {
 | 
			
		||||
  ErrorCodes,
 | 
			
		||||
  callWithErrorHandling,
 | 
			
		||||
  callWithAsyncErrorHandling
 | 
			
		||||
} from './errorHandling'
 | 
			
		||||
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
 | 
			
		||||
import { invokeDirectiveHook } from './directives'
 | 
			
		||||
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(
 | 
			
		||||
  { effect, update }: ComponentInternalInstance,
 | 
			
		||||
  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 { convertLegacyVModelProps } from './compat/componentVModel'
 | 
			
		||||
import { defineLegacyVNodeProperties } from './compat/renderFn'
 | 
			
		||||
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
 | 
			
		||||
 | 
			
		||||
export const Fragment = Symbol(__DEV__ ? 'Fragment' : undefined) as any as {
 | 
			
		||||
  __isFragment: true
 | 
			
		||||
@ -811,3 +812,15 @@ export function mergeProps(...args: (Data & VNodeProps)[]) {
 | 
			
		||||
  }
 | 
			
		||||
  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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user