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

This commit is contained in:
edison 2025-01-29 12:12:44 +08:00 committed by GitHub
parent 8008509aac
commit 139448556d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 53 additions and 16 deletions

View File

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

View File

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

View File

@ -50,6 +50,7 @@ import {
import { import {
type DynamicPropsSource, type DynamicPropsSource,
type RawProps, type RawProps,
getKeysFromRawProps,
getPropsProxyHandlers, getPropsProxyHandlers,
hasFallthroughAttrs, hasFallthroughAttrs,
normalizePropsOptions, normalizePropsOptions,
@ -410,6 +411,14 @@ export class VaporComponentInstance implements GenericComponentInstance {
this.emitsOptions = normalizeEmitsOptions(comp) 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( export function isVaporComponent(