Ver código fonte

refactor(runtime-core): useModel work with vapor mode (#12666)

edison 1 ano atrás
pai
commit
139448556d

+ 6 - 0
packages/runtime-core/src/component.ts

@@ -366,6 +366,7 @@ export interface GenericComponentInstance {
    * @internal
    */
   refs: Data
+  emit: EmitFn
   /**
    * used for keeping track of .once event handlers on components
    * @internal
@@ -377,6 +378,11 @@ export interface GenericComponentInstance {
    * @internal
    */
   propsDefaults: Data | null
+  /**
+   * used for getting the keys of a component's raw props, vapor only
+   * @internal
+   */
+  rawKeys?: () => string[]
 
   // exposed properties via expose()
   exposed: Record<string, any> | null

+ 38 - 16
packages/runtime-core/src/helpers/useModel.ts

@@ -1,7 +1,10 @@
 import { type Ref, customRef, ref } from '@vue/reactivity'
 import { EMPTY_OBJ, camelize, hasChanged, hyphenate } from '@vue/shared'
 import type { DefineModelOptions, ModelRef } from '../apiSetupHelpers'
-import { getCurrentInstance } from '../component'
+import {
+  type ComponentInternalInstance,
+  getCurrentGenericInstance,
+} from '../component'
 import { warn } from '../warning'
 import type { NormalizedProps } from '../componentProps'
 import { watchSyncEffect } from '../apiWatch'
@@ -23,14 +26,14 @@ export function useModel(
   name: string,
   options: DefineModelOptions = EMPTY_OBJ,
 ): Ref {
-  const i = getCurrentInstance()!
+  const i = getCurrentGenericInstance()!
   if (__DEV__ && !i) {
     warn(`useModel() called without active instance.`)
     return ref() as any
   }
 
   const camelizedName = camelize(name)
-  if (__DEV__ && !(i.propsOptions[0] as NormalizedProps)[camelizedName]) {
+  if (__DEV__ && !(i.propsOptions![0] as NormalizedProps)[camelizedName]) {
     warn(`useModel() called with prop "${name}" which is not declared.`)
     return ref() as any
   }
@@ -65,19 +68,38 @@ export function useModel(
         ) {
           return
         }
-        const rawProps = i.vnode!.props
-        if (
-          !(
-            rawProps &&
-            // check if parent has passed v-model
-            (name in rawProps ||
-              camelizedName in rawProps ||
-              hyphenatedName in rawProps) &&
-            (`onUpdate:${name}` in rawProps ||
-              `onUpdate:${camelizedName}` in rawProps ||
-              `onUpdate:${hyphenatedName}` in rawProps)
-          )
-        ) {
+
+        let rawPropKeys
+        let parentPassedModelValue = false
+        let parentPassedModelUpdater = false
+
+        if (i.rawKeys) {
+          // vapor instance
+          rawPropKeys = i.rawKeys()
+        } else {
+          const rawProps = (i as ComponentInternalInstance).vnode!.props
+          rawPropKeys = rawProps && Object.keys(rawProps)
+        }
+
+        if (rawPropKeys) {
+          for (const key of rawPropKeys) {
+            if (
+              key === name ||
+              key === camelizedName ||
+              key === hyphenatedName
+            ) {
+              parentPassedModelValue = true
+            } else if (
+              key === `onUpdate:${name}` ||
+              key === `onUpdate:${camelizedName}` ||
+              key === `onUpdate:${hyphenatedName}`
+            ) {
+              parentPassedModelUpdater = true
+            }
+          }
+        }
+
+        if (!parentPassedModelValue || !parentPassedModelUpdater) {
           // no v-model, local update
           localValue = value
           trigger()

+ 9 - 0
packages/runtime-vapor/src/component.ts

@@ -50,6 +50,7 @@ import {
 import {
   type DynamicPropsSource,
   type RawProps,
+  getKeysFromRawProps,
   getPropsProxyHandlers,
   hasFallthroughAttrs,
   normalizePropsOptions,
@@ -410,6 +411,14 @@ export class VaporComponentInstance implements GenericComponentInstance {
       this.emitsOptions = normalizeEmitsOptions(comp)
     }
   }
+
+  /**
+   * Expose `getKeysFromRawProps` on the instance so it can be used in code
+   * paths where it's needed, e.g. `useModel`
+   */
+  rawKeys(): string[] {
+    return getKeysFromRawProps(this.rawProps)
+  }
 }
 
 export function isVaporComponent(