Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading