import { remove, isDef, hasOwn, isArray, isFunction, invokeWithErrorHandling, warn } from 'core/util' import type { VNodeWithData } from 'types/vnode' import { Component } from 'types/component' import { isRef } from 'v3' export default { create(_: any, vnode: VNodeWithData) { registerRef(vnode) }, update(oldVnode: VNodeWithData, vnode: VNodeWithData) { if (oldVnode.data.ref !== vnode.data.ref) { registerRef(oldVnode, true) registerRef(vnode) } }, destroy(vnode: VNodeWithData) { registerRef(vnode, true) } } export function registerRef(vnode: VNodeWithData, isRemoval?: boolean) { const ref = vnode.data.ref if (!isDef(ref)) return const vm = vnode.context const refValue = vnode.componentInstance || vnode.elm const value = isRemoval ? null : refValue const $refsValue = isRemoval ? undefined : refValue if (isFunction(ref)) { invokeWithErrorHandling(ref, vm, [value], vm, `template ref function`) return } const isFor = vnode.data.refInFor const _isString = typeof ref === 'string' || typeof ref === 'number' const _isRef = isRef(ref) const refs = vm.$refs if (_isString || _isRef) { if (isFor) { const existing = _isString ? refs[ref] : ref.value if (isRemoval) { isArray(existing) && remove(existing, refValue) } else { if (!isArray(existing)) { if (_isString) { refs[ref] = [refValue] setSetupRef(vm, ref, refs[ref]) } else { ref.value = [refValue] } } else if (!existing.includes(refValue)) { existing.push(refValue) } } } else if (_isString) { if (isRemoval && refs[ref] !== refValue) { return } refs[ref] = $refsValue setSetupRef(vm, ref, value) } else if (_isRef) { if (isRemoval && ref.value !== refValue) { return } ref.value = value } else if (__DEV__) { warn(`Invalid template ref type: ${typeof ref}`) } } } function setSetupRef( { _setupState }: Component, key: string | number, val: any ) { if (_setupState && hasOwn(_setupState, key as string)) { if (isRef(_setupState[key])) { _setupState[key].value = val } else { _setupState[key] = val } } }