解决因单向数据流导致的问题

119 阅读1分钟

由于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>