vue3 ui组件子组件封装v-model绑定props modelValue
v-model:在vue3中,是属于“:modelValue”和“update:modelValue”组合的语法糖。
场景:父组件使用子组件通过v-model绑定收集属性值,子组件中的元素,又通过v-mode绑定父组件传递的props属性。
失败原因:在子组件中直接通过v-model绑定modelValue,打破单向数据流,导致“update:modelValue”无法正常工作。
解决1:拆分v-model
<template>
<div>
<UI :modelValue="modelValue" @update:modelValue="handleModelValue"></UI>
</div>
</template>
<script setup>
const props = defineProps({
modelValue: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:modelValue'])
function handleModelValue (val) {
emit('update:modelValue', val)
}
</script>
解决2:computed
<template>
<div>
<UI v-model="uiValue"></UI>
</div>
</template>
<script setup>
import { computed } from "vue"
const props = defineProps({
modelValue: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:modelValue'])
const uiValue = computed({
get () {
return props.modelValue
},
set (val) {
emit('update:modelValue', val)
}
})
</script>
解决3:computed+代理+封装
import { computed } from "vue";
// 缓存(WeakMap避免内存泄漏)
const cacheMap = new WeakMap();
export function useVModel(props, propName, emit) {
return computed({
get() {
const value = props[propName];
// 如果是原始值,直接返回
if (typeof value !== 'object' || value === null) {
return value;
}
// 如果缓存中存在该属性的代理,则返回缓存的代理
if (cacheMap.has(value)) {
return cacheMap.get(value);
}
// 创建代理对象
const proxy = new Proxy(value, {
get(target, key) {
return Reflect.get(target, key);
},
set(target, key, newValue) {
// 更新父组件的状态
emit('update:' + propName, {
...target,
[key]: newValue
});
return true;
}
});
// 将代理对象缓存起来
cacheMap.set(value, proxy);
return proxy;
},
set(newValue) {
// 直接设置新的值,触发父组件的更新
emit('update:' + propName, newValue);
}
});
}
<template>
<div>
<my v-model="modelValue"></UI>
</div>
</template>
<script setup>
const props = defineProps({
modelValue: {
type: String,
default: ''
}
})
</script>
解决4:工具库vueuse(推荐)
官方地址:https://vueuse.nodejs.cn/core/useVModel/
<script lang="ts" setup>
import { useVModel } from '@vueuse/core'
const props = defineProps<{
modelValue: string
}>()
const emit = defineEmits(['update:modelValue'])
const data = useVModel(props, 'modelValue', emit)
</script>
解决5:vue3.4新特性defineModel(推荐)
官方地址:https://cn.vuejs.org/api/sfc-script-setup.html#definemodel
// 声明 "modelValue" prop,由父组件通过 v-model 使用
const model = defineModel()
// 或者:声明带选项的 "modelValue" prop
const model = defineModel({ type: String })
// 在被修改时,触发 "update:modelValue" 事件
model.value = "hello"
// 声明 "count" prop,由父组件通过 v-model:count 使用
const count = defineModel("count")
// 或者:声明带选项的 "count" prop
const count = defineModel("count", { type: Number, default: 0 })
function inc() {
// 在被修改时,触发 "update:count" 事件
count.value++
}