From 1928c9b537ae86b7df5c85276e1551adc50f0746 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 11 Dec 2021 09:05:51 +0800 Subject: [PATCH] refactor: move template ref setter into dedicated file --- ...ef.spec.ts => rendererTemplateRef.spec.ts} | 0 .../runtime-core/src/components/KeepAlive.ts | 11 +- packages/runtime-core/src/hydration.ts | 6 +- packages/runtime-core/src/renderer.ts | 143 +----------------- .../runtime-core/src/rendererTemplateRef.ts | 127 ++++++++++++++++ packages/runtime-core/src/vnode.ts | 13 ++ 6 files changed, 157 insertions(+), 143 deletions(-) rename packages/runtime-core/__tests__/{apiTemplateRef.spec.ts => rendererTemplateRef.spec.ts} (100%) create mode 100644 packages/runtime-core/src/rendererTemplateRef.ts diff --git a/packages/runtime-core/__tests__/apiTemplateRef.spec.ts b/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts similarity index 100% rename from packages/runtime-core/__tests__/apiTemplateRef.spec.ts rename to packages/runtime-core/__tests__/rendererTemplateRef.spec.ts diff --git a/packages/runtime-core/src/components/KeepAlive.ts b/packages/runtime-core/src/components/KeepAlive.ts index 223a7c60..c6180019 100644 --- a/packages/runtime-core/src/components/KeepAlive.ts +++ b/packages/runtime-core/src/components/KeepAlive.ts @@ -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' diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index 517b1495..f405aac4 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -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, diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 073ff91b..04bebdb1 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -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 diff --git a/packages/runtime-core/src/rendererTemplateRef.ts b/packages/runtime-core/src/rendererTemplateRef.ts new file mode 100644 index 00000000..249fb9e5 --- /dev/null +++ b/packages/runtime-core/src/rendererTemplateRef.ts @@ -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})`) + } + } +} diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index a70d93e3..0db4005c 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -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 + ]) +}