]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: ref v-for compat
authorEvan You <yyx990803@gmail.com>
Mon, 26 Apr 2021 21:35:41 +0000 (17:35 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 26 Apr 2021 21:35:41 +0000 (17:35 -0400)
packages/compiler-core/src/compat/compatConfig.ts
packages/compiler-core/src/transforms/transformElement.ts
packages/runtime-core/src/compat/compatConfig.ts
packages/runtime-core/src/compat/ref.ts [new file with mode: 0644]
packages/runtime-core/src/compat/renderFn.ts
packages/runtime-core/src/renderer.ts
packages/runtime-core/src/vnode.ts

index 717b8dc46ad8a50e55dbfe3887019a7edd65499b..d43c441d39a7f679932274098ed0de1d7812fdc0 100644 (file)
@@ -20,6 +20,7 @@ export const enum CompilerDeprecationTypes {
   COMPILER_V_BIND_OBJECT_ORDER = 'COMPILER_V_BIND_OBJECT_ORDER',
   COMPILER_V_ON_NATIVE = 'COMPILER_V_ON_NATIVE',
   COMPILER_V_IF_V_FOR_PRECEDENCE = 'COMPILER_V_IF_V_FOR_PRECEDENCE',
+  COMPILER_V_FOR_REF = 'COMPILER_V_FOR_REF',
   COMPILER_NATIVE_TEMPLATE = 'COMPILER_NATIVE_TEMPLATE',
   COMPILER_INLINE_TEMPLATE = 'COMPILER_INLINE_TEMPLATE',
   COMPILER_FILTERS = 'COMPILER_FILTER'
@@ -78,6 +79,13 @@ const deprecationData: Record<CompilerDeprecationTypes, DeprecationData> = {
     link: `https://v3.vuejs.org/guide/migration/v-if-v-for.html`
   },
 
+  [CompilerDeprecationTypes.COMPILER_V_FOR_REF]: {
+    message:
+      `Ref usage on v-for no longer creates array ref values in Vue 3. ` +
+      `Consider using function refs or refactor to avoid ref usage altogether.`,
+    link: `https://v3.vuejs.org/guide/migration/array-refs.html`
+  },
+
   [CompilerDeprecationTypes.COMPILER_NATIVE_TEMPLATE]: {
     message:
       `<template> with no special directives will render as a native template ` +
index d0ceaa3939b84ca43e015ee73d032e008a9eac17..bf040467d007e2e4f66e41f5823251120ecaa144 100644 (file)
@@ -590,6 +590,25 @@ export function buildProps(
         runtimeDirectives.push(prop)
       }
     }
+
+    if (
+      __COMPAT__ &&
+      prop.type === NodeTypes.ATTRIBUTE &&
+      prop.name === 'ref' &&
+      context.scopes.vFor > 0 &&
+      checkCompatEnabled(
+        CompilerDeprecationTypes.COMPILER_V_FOR_REF,
+        context,
+        prop.loc
+      )
+    ) {
+      properties.push(
+        createObjectProperty(
+          createSimpleExpression('refInFor', true),
+          createSimpleExpression('true', false)
+        )
+      )
+    }
   }
 
   let propsExpression: PropsExpression | undefined = undefined
index e036be5feef9d03df3792d9becf68d42b27cb2bb..a163bf29420f9083a3134a5b3088fc4eeba35dc8 100644 (file)
@@ -44,6 +44,7 @@ export const enum DeprecationTypes {
   WATCH_ARRAY = 'WATCH_ARRAY',
   PROPS_DEFAULT_THIS = 'PROPS_DEFAULT_THIS',
 
+  V_FOR_REF = 'V_FOR_REF',
   V_ON_KEYCODE_MODIFIER = 'V_ON_KEYCODE_MODIFIER',
   CUSTOM_DIR = 'CUSTOM_DIR',
 
@@ -287,6 +288,13 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
     link: `https://v3.vuejs.org/guide/migration/custom-directives.html`
   },
 
+  [DeprecationTypes.V_FOR_REF]: {
+    message:
+      `Ref usage on v-for no longer creates array ref values in Vue 3. ` +
+      `Consider using function refs or refactor to avoid ref usage altogether.`,
+    link: `https://v3.vuejs.org/guide/migration/array-refs.html`
+  },
+
   [DeprecationTypes.V_ON_KEYCODE_MODIFIER]: {
     message:
       `Using keyCode as v-on modifier is no longer supported. ` +
@@ -447,16 +455,18 @@ export function warnDeprecation(
   }
 
   const dupKey = key + args.join('')
-  const compName = instance && formatComponentName(instance, instance.type)
+  let compId: string | number | null =
+    instance && formatComponentName(instance, instance.type)
+  if (compId === 'Anonymous' && instance) {
+    compId = instance.uid
+  }
 
   // skip if the same warning is emitted for the same component type
-  if (compName !== `Anonymous`) {
-    const componentDupKey = dupKey + compName
-    if (componentDupKey in instanceWarned) {
-      return
-    }
-    instanceWarned[componentDupKey] = true
+  const componentDupKey = dupKey + compId
+  if (componentDupKey in instanceWarned) {
+    return
   }
+  instanceWarned[componentDupKey] = true
 
   // same warning, but different component. skip the long message and just
   // log the key and count.
diff --git a/packages/runtime-core/src/compat/ref.ts b/packages/runtime-core/src/compat/ref.ts
new file mode 100644 (file)
index 0000000..7b82712
--- /dev/null
@@ -0,0 +1,45 @@
+import { isArray, remove } from '@vue/shared'
+import { ComponentInternalInstance, Data } from '../component'
+import { VNode } from '../vnode'
+import { DeprecationTypes, warnDeprecation } from './compatConfig'
+
+export function convertLegacyRefInFor(vnode: VNode) {
+  // refInFor
+  if (vnode.props && vnode.props.refInFor) {
+    delete vnode.props.refInFor
+    if (vnode.ref) {
+      if (isArray(vnode.ref)) {
+        vnode.ref.forEach(r => (r.f = true))
+      } else {
+        vnode.ref.f = true
+      }
+    }
+  }
+}
+
+export function registerLegacyRef(
+  refs: Data,
+  key: string,
+  value: any,
+  owner: ComponentInternalInstance,
+  isInFor: boolean | undefined,
+  isUnmount: boolean
+) {
+  const existing = refs[key]
+  if (isUnmount) {
+    if (isArray(existing)) {
+      remove(existing, value)
+    } else {
+      refs[key] = null
+    }
+  } else if (isInFor) {
+    __DEV__ && warnDeprecation(DeprecationTypes.V_FOR_REF, owner)
+    if (!isArray(existing)) {
+      refs[key] = [value]
+    } else if (!existing.includes(value)) {
+      existing.push(value)
+    }
+  } else {
+    refs[key] = value
+  }
+}
index 7f69c64e9f109692b64ee693922f5c24bbcc0eb0..c4dbcf294c891d7e3926209ae352ea8684f84731 100644 (file)
@@ -171,7 +171,7 @@ export function compatH(
 }
 
 const skipLegacyRootLevelProps = /*#__PURE__*/ makeMap(
-  'refInFor,staticStyle,staticClass,directives,model'
+  'staticStyle,staticClass,directives,model,hook'
 )
 
 function convertLegacyProps(
@@ -206,8 +206,6 @@ function convertLegacyProps(
           }
         }
       }
-    } else if (key === 'hook') {
-      // TODO
     } else if (!skipLegacyRootLevelProps(key)) {
       converted[key] = legacyProps[key as keyof LegacyVNodeProps]
     }
index 2a2ac944da726cfac5c161ababb0e31408690a8d..817945986c8995de86ea61b96945894aec4f5643 100644 (file)
@@ -76,7 +76,6 @@ import {
 import { createHydrationFunctions, RootHydrateFunction } from './hydration'
 import { invokeDirectiveHook } from './directives'
 import { startMeasure, endMeasure } from './profiling'
-import { ComponentPublicInstance } from './componentPublicInstance'
 import {
   devtoolsComponentAdded,
   devtoolsComponentRemoved,
@@ -87,6 +86,7 @@ import { initFeatureFlags } from './featureFlags'
 import { isAsyncWrapper } from './apiAsyncComponent'
 import { isCompatEnabled } from './compat/compatConfig'
 import { DeprecationTypes } from './compat/compatConfig'
+import { registerLegacyRef } from './compat/ref'
 
 export interface Renderer<HostElement = RendererElement> {
   render: RootRenderFunction<HostElement>
@@ -309,7 +309,8 @@ export const setRef = (
   rawRef: VNodeNormalizedRef,
   oldRawRef: VNodeNormalizedRef | null,
   parentSuspense: SuspenseBoundary | null,
-  vnode: VNode | null
+  vnode: VNode,
+  isUnmount = false
 ) => {
   if (isArray(rawRef)) {
     rawRef.forEach((r, i) =>
@@ -317,26 +318,25 @@ export const setRef = (
         r,
         oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef),
         parentSuspense,
-        vnode
+        vnode,
+        isUnmount
       )
     )
     return
   }
 
-  let value: ComponentPublicInstance | RendererNode | Record<string, any> | null
-  if (!vnode) {
-    // means unmount
-    value = null
-  } else if (isAsyncWrapper(vnode)) {
+  if (isAsyncWrapper(vnode) && !isUnmount) {
     // when mounting async components, nothing needs to be done,
     // because the template ref is forwarded to inner component
     return
-  } else if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
-    value = vnode.component!.exposed || vnode.component!.proxy
-  } else {
-    value = vnode.el
   }
 
+  const refValue =
+    vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
+      ? vnode.component!.exposed || vnode.component!.proxy
+      : vnode.el
+  const value = isUnmount ? null : refValue
+
   const { i: owner, r: ref } = rawRef
   if (__DEV__ && !owner) {
     warn(
@@ -349,7 +349,7 @@ export const setRef = (
   const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
   const setupState = owner.setupState
 
-  // unset old ref
+  // dynamic ref changed. unset old ref
   if (oldRef != null && oldRef !== ref) {
     if (isString(oldRef)) {
       refs[oldRef] = null
@@ -363,7 +363,11 @@ export const setRef = (
 
   if (isString(ref)) {
     const doSet = () => {
-      refs[ref] = value
+      if (__COMPAT__ && isCompatEnabled(DeprecationTypes.V_FOR_REF, owner)) {
+        registerLegacyRef(refs, ref, refValue, owner, rawRef.f, isUnmount)
+      } else {
+        refs[ref] = value
+      }
       if (hasOwn(setupState, ref)) {
         setupState[ref] = value
       }
@@ -584,7 +588,7 @@ function baseCreateRenderer(
 
     // set ref
     if (ref != null && parentComponent) {
-      setRef(ref, n1 && n1.ref, parentSuspense, n2)
+      setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2)
     }
   }
 
@@ -2113,7 +2117,7 @@ function baseCreateRenderer(
     } = vnode
     // unset ref
     if (ref != null) {
-      setRef(ref, null, parentSuspense, null)
+      setRef(ref, null, parentSuspense, vnode, true)
     }
 
     if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
index 05fc6ad079bd65b26297e447818fc76a4bb2c361..dc002225b25b755b5a186cf86f7bdd90994b55b1 100644 (file)
@@ -44,6 +44,7 @@ import { setCompiledSlotRendering } from './helpers/renderSlot'
 import { convertLegacyComponent } from './compat/component'
 import { convertLegacyVModelProps } from './compat/vModel'
 import { defineLegacyVNodeProperties } from './compat/renderFn'
+import { convertLegacyRefInFor } from './compat/ref'
 
 export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
   __isFragment: true
@@ -74,6 +75,7 @@ export type VNodeRef =
 export type VNodeNormalizedRefAtom = {
   i: ComponentInternalInstance
   r: VNodeRef
+  f?: boolean // v2 compat only, refInFor marker
 }
 
 export type VNodeNormalizedRef =
@@ -130,10 +132,12 @@ export interface VNode<
    * @internal
    */
   __v_isVNode: true
+
   /**
    * @internal
    */
   [ReactiveFlags.SKIP]: true
+
   type: VNodeTypes
   props: (VNodeProps & ExtraProps) | null
   key: string | number | null
@@ -413,7 +417,7 @@ function _createVNode(
 
   const vnode: VNode = {
     __v_isVNode: true,
-    [ReactiveFlags.SKIP]: true,
+    __v_skip: true,
     type,
     props,
     key: props && normalizeKey(props),
@@ -473,6 +477,7 @@ function _createVNode(
 
   if (__COMPAT__) {
     convertLegacyVModelProps(vnode)
+    convertLegacyRefInFor(vnode)
     defineLegacyVNodeProperties(vnode)
   }
 
@@ -490,7 +495,7 @@ export function cloneVNode<T, U>(
   const mergedProps = extraProps ? mergeProps(props || {}, extraProps) : props
   const cloned: VNode = {
     __v_isVNode: true,
-    [ReactiveFlags.SKIP]: true,
+    __v_skip: true,
     type: vnode.type,
     props: mergedProps,
     key: mergedProps && normalizeKey(mergedProps),