Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | 16x 16x 16x 16x 16x 16x 16x 16x 16x 29x 29x 28x 28x 16x 62x 62x 30x 30x 1x 29x 29x 30x 4x 4x 29x 29x 6x 29x 29x 16x 2x 2x 4x 4x 16x 94x 62x | 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 { warn } from '../warning'
import type { NormalizedProps } from '../componentProps'
import { watchSyncEffect } from '../apiWatch'
export function useModel<
M extends PropertyKey,
T extends Record<string, any>,
K extends keyof T,
G = T[K],
S = T[K],
>(
props: T,
name: K,
options?: DefineModelOptions<T[K], G, S>,
): ModelRef<T[K], M, G, S>
export function useModel(
props: Record<string, any>,
name: string,
options: DefineModelOptions = EMPTY_OBJ,
): Ref {
const i = getCurrentInstance()!
Iif (__DEV__ && !i) {
warn(`useModel() called without active instance.`)
return ref() as any
}
const camelizedName = camelize(name)
Iif (__DEV__ && !(i.propsOptions[0] as NormalizedProps)[camelizedName]) {
warn(`useModel() called with prop "${name}" which is not declared.`)
return ref() as any
}
const hyphenatedName = hyphenate(name)
const modifiers = getModelModifiers(props, camelizedName)
const res = customRef((track, trigger) => {
let localValue: any
let prevSetValue: any = EMPTY_OBJ
let prevEmittedValue: any
watchSyncEffect(() => {
const propValue = props[camelizedName]
if (hasChanged(localValue, propValue)) {
localValue = propValue
trigger()
}
})
return {
get() {
track()
return options.get ? options.get(localValue) : localValue
},
set(value) {
const emittedValue = options.set ? options.set(value) : value
if (
!hasChanged(emittedValue, localValue) &&
!(prevSetValue !== EMPTY_OBJ && hasChanged(value, prevSetValue))
) {
return
}
const rawProps = i.vnode!.props
const hasVModel = !!(
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)
)
if (!hasVModel) {
// no v-model, local update
localValue = value
trigger()
}
i.emit(`update:${name}`, emittedValue)
// #10279: if the local value is converted via a setter but the value
// emitted to parent was the same, the parent will not trigger any
// updates and there will be no prop sync. However the local input state
// may be out of sync, so we need to force an update here.
if (
hasChanged(value, prevSetValue) &&
((hasChanged(value, emittedValue) &&
!hasChanged(emittedValue, prevEmittedValue)) ||
// #13524: browsers differ in when they flush microtasks between
// event listeners. If a v-model listener emits an intermediate value
// and a following listener restores the model to its previous prop
// value before parent updates are flushed, the parent render can be
// deduped as having no prop change. Force a local update so DOM state
// such as an input's value is synchronized back to the current model.
(hasVModel &&
prevSetValue !== EMPTY_OBJ &&
!hasChanged(emittedValue, localValue)))
) {
trigger()
}
prevSetValue = value
prevEmittedValue = emittedValue
},
}
})
// @ts-expect-error
res[Symbol.iterator] = () => {
let i = 0
return {
next() {
if (i < 2) {
return { value: i++ ? modifiers || EMPTY_OBJ : res, done: false }
} else E{
return { done: true }
}
},
}
}
return res
}
export const getModelModifiers = (
props: Record<string, any>,
modelName: string,
): Record<string, boolean> | undefined => {
return modelName === 'modelValue' || modelName === 'model-value'
? props.modelModifiers
: props[`${modelName}Modifiers`] ||
props[`${camelize(modelName)}Modifiers`] ||
props[`${hyphenate(modelName)}Modifiers`]
}
|