Vue3自定义手机号格式化输入组件无法与父组件v-model双向绑定求助
问题根因
Vue3 自定义组件默认的 v-model 绑定逻辑和 Vue2 不同:默认会向子组件传递名为 modelValue 的 prop,子组件需要触发 update:modelValue 事件才能将修改后的值同步回父组件。你当前的实现完全没有处理这两个逻辑,内部维护的 number 是独立的局部状态,自然无法和父组件的绑定值同步。
最优解决方案
推荐使用带 get/set 的计算属性实现,既能保留你的格式化逻辑,也不需要额外维护多份状态避免同步冲突,修改后的完整组件代码如下:
<template> <div class="relative flex items-center w-full" style="direction: ltr"> <span class="absolute block ml-3 pointer-events-none">+20</span> <input ref="input" v-model="formattedNumber" type="text" class="w-full border-gray-300 py-2 pr-3 pl-10"> </div> </template> <script> import { computed } from 'vue'; export default { name: 'XPhoneInput', // 声明接收v-model默认传入的属性 props: { modelValue: String, default: '' }, // 声明需要触发的同步事件 emits: ['update:modelValue'], setup(props, { emit }) { // 用计算属性衔接父组件传值和内部格式化逻辑 const formattedNumber = computed({ get() { // 取值时先格式化,保证内外显示一致 return props.modelValue.replace(/[^0-9]/g, '') .replace(/^(\d{2})?(\d{4})?(\d{4})?/g, '$1 $2 $3') .substr(0, 12); }, set(val) { // 输入变化时先格式化再同步给父组件 const processed = val.replace(/[^0-9]/g, '') .replace(/^(\d{2})?(\d{4})?(\d{4})?/g, '$1 $2 $3') .substr(0, 12); emit('update:modelValue', processed); } }); return { formattedNumber }; } }; </script>
父组件调用方式不需要做任何修改,原写法 <x-phone-input v-model="form.phone" /> 可以直接生效。
替代方案(保留原有watch写法)
如果你不想调整现有的watch实现逻辑,也可以按照如下步骤修改:
- 声明props接收父组件传入的
modelValue,声明触发事件update:modelValue - 内部
number初始值取props.modelValue - 新增对
props.modelValue的监听,父组件侧值修改时同步更新内部number - 内部
number变化触发watch时,除了格式化,还要调用emit('update:modelValue', number.value)同步值到父组件
这种方式需要额外处理双向状态同步,容易出现循环更新问题,更推荐用计算属性方案。
内容的提问来源于stack exchange,提问作者The_ashraf
相关产品推荐
相关产品推荐

