vue3中自定一个组件并且能够用v-model对自定义组件进行数据的双向绑定

1. 基础用法

在 Vue3 中,v-model 在组件上的使用有了更灵活的方式。默认情况下,v-model 使用 modelValue 作为 prop,update:modelValue 作为事件。

1.1 基本示例

vue 复制代码
<!-- CustomInput.vue -->
<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>

<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>

<!-- 父组件使用 -->
<template>
  <CustomInput v-model="searchText" />
</template>

<script setup>
import { ref } from 'vue'
const searchText = ref('')
</script>

1.2 使用 computed 实现双向绑定

vue 复制代码
<!-- CustomInput.vue -->
<template>
  <input v-model="inputValue" />
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

// 使用计算属性实现双向绑定
const inputValue = computed({
  get() {
    return props.modelValue
  },
  set(value) {
    emit('update:modelValue', value)
  }
})
</script>

2. 自定义 v-model 名称

Vue3 允许我们在同一个组件上使用多个 v-model,每个 v-model 可以有自己的名称。

2.1 单个自定义名称

vue 复制代码
<!-- CustomField.vue -->
<template>
  <input
    :value="title"
    @input="$emit('update:title', $event.target.value)"
  />
</template>

<script setup>
defineProps(['title'])
defineEmits(['update:title'])
</script>

<!-- 父组件使用 -->
<template>
  <CustomField v-model:title="articleTitle" />
</template>

<script setup>
import { ref } from 'vue'
const articleTitle = ref('默认标题')
</script>

2.2 多个 v-model 绑定

vue 复制代码
<!-- UserForm.vue -->
<template>
  <div class="form">
    <input
      :value="firstName"
      @input="$emit('update:firstName', $event.target.value)"
    />
    <input
      :value="lastName"
      @input="$emit('update:lastName', $event.target.value)"
    />
  </div>
</template>

<script setup>
defineProps(['firstName', 'lastName'])
defineEmits(['update:firstName', 'update:lastName'])
</script>

<!-- 父组件使用 -->
<template>
  <UserForm
    v-model:firstName="user.firstName"
    v-model:lastName="user.lastName"
  />
</template>

<script setup>
import { reactive } from 'vue'

const user = reactive({
  firstName: 'John',
  lastName: 'Doe'
})
</script>

3. v-model 修饰符

3.1 内置修饰符

vue 复制代码
<!-- CustomInput.vue -->
<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>

<script setup>
defineProps({
  modelValue: String,
  modelModifiers: { default: () => ({}) }
})
</script>

<!-- 父组件使用 -->
<template>
  <CustomInput v-model.trim="text" />
</template>

3.2 自定义修饰符

vue 复制代码
<!-- CustomInput.vue -->
<template>
  <input
    :value="modelValue"
    @input="handleInput"
  />
</template>

<script setup>
const props = defineProps({
  modelValue: String,
  modelModifiers: { default: () => ({}) }
})

const emit = defineEmits(['update:modelValue'])

const handleInput = (event) => {
  let value = event.target.value
  // 检查是否应用了 capitalize 修饰符
  if (props.modelModifiers.capitalize) {
    value = value.charAt(0).toUpperCase() + value.slice(1)
  }
  emit('update:modelValue', value)
}
</script>

<!-- 父组件使用 -->
<template>
  <CustomInput v-model.capitalize="text" />
</template>

4. 实际应用示例

4.1 自定义数字输入组件

vue 复制代码
<!-- NumberInput.vue -->
<template>
  <div class="number-input">
    <button @click="decrease">-</button>
    <input
      type="number"
      :value="modelValue"
      @input="handleInput"
      :step="step"
    />
    <button @click="increase">+</button>
  </div>
</template>

<script setup>
const props = defineProps({
  modelValue: {
    type: Number,
    required: true
  },
  step: {
    type: Number,
    default: 1
  },
  min: {
    type: Number,
    default: -Infinity
  },
  max: {
    type: Number,
    default: Infinity
  }
})

const emit = defineEmits(['update:modelValue'])

const handleInput = (event) => {
  const value = Number(event.target.value)
  if (isValidValue(value)) {
    emit('update:modelValue', value)
  }
}

const increase = () => {
  const newValue = props.modelValue + props.step
  if (isValidValue(newValue)) {
    emit('update:modelValue', newValue)
  }
}

const decrease = () => {
  const newValue = props.modelValue - props.step
  if (isValidValue(newValue)) {
    emit('update:modelValue', newValue)
  }
}

const isValidValue = (value) => {
  return value >= props.min && value <= props.max
}
</script>

<!-- 父组件使用 -->
<template>
  <NumberInput
    v-model="quantity"
    :step="1"
    :min="0"
    :max="100"
  />
</template>

<script setup>
import { ref } from 'vue'
const quantity = ref(1)
</script>

4.2 颜色选择器组件

vue 复制代码
<!-- ColorPicker.vue -->
<template>
  <div class="color-picker">
    <div
      class="color-preview"
      :style="{ backgroundColor: modelValue }"
    ></div>
    <input
      type="color"
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    />
    <input
      type="text"
      :value="modelValue"
      @input="handleInput"
      placeholder="#000000"
    />
  </div>
</template>

<script setup>
const props = defineProps({
  modelValue: {
    type: String,
    default: '#000000'
  }
})

const emit = defineEmits(['update:modelValue'])

const handleInput = (event) => {
  const value = event.target.value
  // 验证是否为有效的颜色值
  if (/^#[0-9A-Fa-f]{6}$/.test(value)) {
    emit('update:modelValue', value)
  }
}
</script>

<!-- 父组件使用 -->
<template>
  <ColorPicker v-model="themeColor" />
</template>

<script setup>
import { ref } from 'vue'
const themeColor = ref('#42b883')
</script>

5. 最佳实践

  1. 命名规范

    • 使用语义化的 prop 名称
    • 保持事件名称与 prop 名称的一致性
    • 使用 update: 前缀作为事件名称
  2. 类型检查

    • 为 props 定义明确的类型
    • 在必要时添加自定义验证
    • 处理无效输入
  3. 值的验证

    • 在更新值之前进行验证
    • 提供适当的错误反馈
    • 实现合理的默认值处理
  4. 性能优化

    • 避免不必要的值更新
    • 使用计算属性优化复杂逻辑
    • 合理使用防抖和节流
相关推荐
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅12 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅12 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment12 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅12 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊12 小时前
jwt介绍
前端
爱敲代码的小鱼12 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax