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
*/
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

View File

@ -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()

View File

@ -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(