vue3 ui组件子组件封装v-model绑定props modelValue

vue3中,v-model是属于 :modelValueupdate: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++
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值