# ref 的设置和重设

export const setRef = (
  rawRef: VNodeNormalizedRef,
  oldRawRef: VNodeNormalizedRef | null,
  parentComponent: ComponentInternalInstance,
  parentSuspense: SuspenseBoundary | null,
  vnode: VNode | null
) => {
  let value: ComponentPublicInstance | RendererNode | null;
  if (!vnode) {
    value = null;
  } else {
    if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
      value = vnode.component!.proxy;
    } else {
      value = vnode.el;
    }
  }

  const [owner, ref] = rawRef;
  const oldRef = oldRawRef && oldRawRef[1];
  const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs;
  const setupState = owner.setupState;

  // unset old ref
  if (oldRef != null && oldRef !== ref) {
    if (isString(oldRef)) {
      refs[oldRef] = null;
      if (hasOwn(setupState, oldRef)) {
        queuePostRenderEffect(() => {
          setupState[oldRef] = null;
        }, parentSuspense);
      }
    } else if (isRef(oldRef)) {
      oldRef.value = null;
    }
  }

  if (isString(ref)) {
    refs[ref] = value;
    if (hasOwn(setupState, ref)) {
      queuePostRenderEffect(() => {
        setupState[ref] = value;
      }, parentSuspense);
    }
  } else if (isRef(ref)) {
    ref.value = value;
  } else if (isFunction(ref)) {
    callWithErrorHandling(ref, parentComponent, ErrorCodes.FUNCTION_REF, [
      value,
      refs,
    ]);
  } else if (__DEV__) {
    warn("Invalid template ref type:", value, `(${typeof value})`);
  }
};

首先获取 value,对于有状态组件,value 是组件的实例的 proxy。而对于无状态组件,则是他的根 dom 节点,vnode.el

然后卸载老的 ref,再挂载新的 ref,这里可以看到 ref 可以接受两种值。

  • string
  • ref

ref可以通过const myRef = ref()进行创建,然后直接把myRef当作参数传递就可以,通过myRef.value就可以获取节点的值。而对于 string 的 ref,则只能在this.$refs上进行挂载了。