Skip to content

Commit

Permalink
refactor(runtime-core): useModel work with vapor mode (#12666)
Browse files Browse the repository at this point in the history
  • Loading branch information
edison1105 authored Jan 29, 2025
1 parent 8008509 commit 1394485
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 16 deletions.
6 changes: 6 additions & 0 deletions packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ export interface GenericComponentInstance {
* @internal
*/
refs: Data
emit: EmitFn
/**
* used for keeping track of .once event handlers on components
* @internal
Expand All @@ -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
Expand Down
54 changes: 38 additions & 16 deletions packages/runtime-core/src/helpers/useModel.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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
}
Expand Down Expand Up @@ -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()
Expand Down
9 changes: 9 additions & 0 deletions packages/runtime-vapor/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
import {
type DynamicPropsSource,
type RawProps,
getKeysFromRawProps,
getPropsProxyHandlers,
hasFallthroughAttrs,
normalizePropsOptions,
Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit 1394485

Please sign in to comment.