rendererTemplateRef.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import { SuspenseBoundary } from './components/Suspense'
  2. import { VNode, VNodeNormalizedRef, VNodeNormalizedRefAtom } from './vnode'
  3. import {
  4. EMPTY_OBJ,
  5. hasOwn,
  6. isArray,
  7. isFunction,
  8. isString,
  9. remove,
  10. ShapeFlags
  11. } from '@vue/shared'
  12. import { isAsyncWrapper } from './apiAsyncComponent'
  13. import { getExposeProxy } from './component'
  14. import { warn } from './warning'
  15. import { isRef } from '@vue/reactivity'
  16. import { callWithErrorHandling, ErrorCodes } from './errorHandling'
  17. import { SchedulerJob } from './scheduler'
  18. import { queuePostRenderEffect } from './renderer'
  19. /**
  20. * Function for handling a template ref
  21. */
  22. export function setRef(
  23. rawRef: VNodeNormalizedRef,
  24. oldRawRef: VNodeNormalizedRef | null,
  25. parentSuspense: SuspenseBoundary | null,
  26. vnode: VNode,
  27. isUnmount = false
  28. ) {
  29. if (isArray(rawRef)) {
  30. rawRef.forEach((r, i) =>
  31. setRef(
  32. r,
  33. oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef),
  34. parentSuspense,
  35. vnode,
  36. isUnmount
  37. )
  38. )
  39. return
  40. }
  41. if (isAsyncWrapper(vnode) && !isUnmount) {
  42. // when mounting async components, nothing needs to be done,
  43. // because the template ref is forwarded to inner component
  44. return
  45. }
  46. const refValue =
  47. vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
  48. ? getExposeProxy(vnode.component!) || vnode.component!.proxy
  49. : vnode.el
  50. const value = isUnmount ? null : refValue
  51. const { i: owner, r: ref } = rawRef
  52. if (__DEV__ && !owner) {
  53. warn(
  54. `Missing ref owner context. ref cannot be used on hoisted vnodes. ` +
  55. `A vnode with ref must be created inside the render function.`
  56. )
  57. return
  58. }
  59. const oldRef = oldRawRef && (oldRawRef as VNodeNormalizedRefAtom).r
  60. const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
  61. const setupState = owner.setupState
  62. // dynamic ref changed. unset old ref
  63. if (oldRef != null && oldRef !== ref) {
  64. if (isString(oldRef)) {
  65. refs[oldRef] = null
  66. if (hasOwn(setupState, oldRef)) {
  67. setupState[oldRef] = null
  68. }
  69. } else if (isRef(oldRef)) {
  70. oldRef.value = null
  71. }
  72. }
  73. if (isFunction(ref)) {
  74. callWithErrorHandling(ref, owner, ErrorCodes.FUNCTION_REF, [value, refs])
  75. } else {
  76. const _isString = isString(ref)
  77. const _isRef = isRef(ref)
  78. if (_isString || _isRef) {
  79. const doSet = () => {
  80. if (rawRef.f) {
  81. const existing = _isString
  82. ? hasOwn(setupState, ref)
  83. ? setupState[ref]
  84. : refs[ref]
  85. : ref.value
  86. if (isUnmount) {
  87. isArray(existing) && remove(existing, refValue)
  88. } else {
  89. if (!isArray(existing)) {
  90. if (_isString) {
  91. refs[ref] = [refValue]
  92. if (hasOwn(setupState, ref)) {
  93. setupState[ref] = refs[ref]
  94. }
  95. } else {
  96. ref.value = [refValue]
  97. if (rawRef.k) refs[rawRef.k] = ref.value
  98. }
  99. } else if (!existing.includes(refValue)) {
  100. existing.push(refValue)
  101. }
  102. }
  103. } else if (_isString) {
  104. refs[ref] = value
  105. if (hasOwn(setupState, ref)) {
  106. setupState[ref] = value
  107. }
  108. } else if (_isRef) {
  109. ref.value = value
  110. if (rawRef.k) refs[rawRef.k] = value
  111. } else if (__DEV__) {
  112. warn('Invalid template ref type:', ref, `(${typeof ref})`)
  113. }
  114. }
  115. if (value) {
  116. // #1789: for non-null values, set them after render
  117. // null values means this is unmount and it should not overwrite another
  118. // ref with the same key
  119. ;(doSet as SchedulerJob).id = -1
  120. queuePostRenderEffect(doSet, parentSuspense)
  121. } else {
  122. doSet()
  123. }
  124. } else if (__DEV__) {
  125. warn('Invalid template ref type:', ref, `(${typeof ref})`)
  126. }
  127. }
  128. }