//使用computed 拦截v-model,多用于表单
// 为了性能做缓存,针对每个对象,进行缓存
const cacheMap = new WeakMap()
export function useVModel(props, propName, emit) {
return computed({
get() {
// 如果缓存中有对应的代理就不创建新的代理了
if (cacheMap.has(props[propName])) {
return cacheMap.get(props[propName])
}
const proxy = new Proxy(props[propName], {
get(target, key) {
return Reflect.get(target, key)
},
set(target, key, value) {
emit('update:') + propName,
{
...target,
[key]: value
}
return true
}
})
// 如果缓存中没有对应的代理就创建新的代理
cacheMap.set(props[propName], proxy)
return proxy
},
set(val) {
emit('update:' + propName, val)
}
})
}
interface IEmit {
(event: 'update:modelValue', value: string | number): void
}
interface IProps {
modelValue: string | number
options: LabelValue<string | number>[]
type?: 'radio' | 'select'
disabled?: boolean
}
const emit = defineEmits<IEmit>()
const props = withDefaults(defineProps<IProps>(), {
type: 'select',
disabled: false
})
const value = useVModel(props, 'modelValue', emit)
方案二
import type { UnwrapRef } from 'vue'
//保护了单项数据流,兼顾了可读性
export default function useVModel<
P extends object,
N extends keyof P,
E extends (event: string, value: P[N]) => void
>(props: P, name: N, emit: E) {
const model = ref(props[name])
watch(
() => props[name],
() => {
model.value = props[name] as UnwrapRef<P[N]>
}
)
watch(
model,
() => {
emit(`update:${name as string}`, model.value as P[N])
},
{ deep: true }
)
return model
}