import { remove, isDef, hasOwn, isArray, isFunction, invokeWithErrorHandling, warn } from 'core/util' import type { KNodeWithData } from 'types/knode' import { Component } from 'types/component' import { isRef } from 'v3' export default { create(_: any, knode: KNodeWithData) { registerRef(knode) }, update(oldKnode: KNodeWithData, knode: KNodeWithData) { if (oldKnode.data.ref !== knode.data.ref) { registerRef(oldKnode, true) registerRef(knode) } }, destroy(knode: KNodeWithData) { registerRef(knode, true) } } export function registerRef(knode: KNodeWithData, isRemoval?: boolean) { const ref = knode.data.ref if (!isDef(ref)) return const vm = knode.context const refValue = knode.componentInstance || knode.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 = knode.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 } } }