由于Vue中的单向数据流原则,在一些特定的情况下,例如:父子组件间数据的双向绑定,在子组件中一些表单元素会绑定到父组件传过来的数据,这时,当表单改变导致数据改变时,便打破了单向数据流的原则
<script setup>
import Child from '@/components/Child.vue';
import { ref } from 'vue'
const modelValue = ref({
keyword: '开始'
})
</script>
<template>
<div>
<Child :modelValue="modelValue"/>
</div>
</template>
<template>
<div>
<MyInput v-model="modelValue.keyword" />
</div>
</template>
<script setup>
import MyInput from './MyInput.vue'
const props = defineProps({
modelValue: {
type: Object,
required: true
}
})
</script>
一般来说,我们会通过computed方法来解决单向数据流的问题:
<script setup>
import Child from '@/components/Child.vue';
import { ref } from 'vue'
const modelValue = ref({
keyword: '开始'
})
</script>
<template>
<div>
<Child v-model="modelValue"/>
</div>
</template>
<template>
<div>
<MyInput v-model="keyword" />
</div>
</template>
<script setup>
import MyInput from './MyInput.vue'
import { computed } from 'vue'
const props = defineProps({
modelValue: {
type: Object,
required: true
}
})
const keyword = computed({
get() {
return props.modelValue.keyword
},
set(val) {
emit('update:modelValue',{
...props.modelValue,
keyword: val
})
}
})
</script>
这样去写代码也不是很合适,优化后的代码:
import { computed } from 'vue'
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)
}
})
}
<template>
<div>
<MyInput v-model="model.keyword" />
</div>
</template>
<script setup>
import MyInput from './MyInput.vue'
import { useVModel } from '../plugins/useVModel'
const props = defineProps({
modelValue: {
type: Object,
required: true
}
})
const emits = defineEmits(['update:modelValue'])
const model = useVModel(props,'modelValue',emits)
</script>