From 139448556dd10e12ab31941eebbeaaa308010c20 Mon Sep 17 00:00:00 2001 From: edison Date: Wed, 29 Jan 2025 12:12:44 +0800 Subject: [PATCH] refactor(runtime-core): useModel work with vapor mode (#12666) --- packages/runtime-core/src/component.ts | 6 +++ packages/runtime-core/src/helpers/useModel.ts | 54 +++++++++++++------ packages/runtime-vapor/src/component.ts | 9 ++++ 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index f91af6c7399..493d08b5a05 100644 --- a/packages/runtime-core/src/component.ts +++ b/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 | null diff --git a/packages/runtime-core/src/helpers/useModel.ts b/packages/runtime-core/src/helpers/useModel.ts index befe7627747..e85edc6e9a7 100644 --- a/packages/runtime-core/src/helpers/useModel.ts +++ b/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() diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 61476a098c9..abe72e79cfe 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/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(