Vue3中使用Cleave.js无需v-model.lazy实现值同步的方案
解决Cleave.js格式化后v-model值不同步问题(无需v-model.lazy)
问题描述
使用Cleave.js为输入数字添加千分位分隔符时,输入1000.123会在输入框显示正确格式1,000.12,但v-model绑定的值仍是1000.123而非格式化后的1000.12。不想使用v-model.lazy(该修饰符仅在输入框失焦时更新值),希望实现实时同步格式化后的值。
当前组件代码
<script setup lang="ts"> import { ref, watch } from 'vue' const props = withDefaults(defineProps<Props>(), { type: 'text', required: false, readonly: false, disabled: false }) const inputValue = ref(props.modelValue) const emit = defineEmits<{ (e: 'update:modelValue', value: string | number): void }>() watch( () => props.modelValue, () => { inputValue.value = numeric.format(props.modelValue) }, { immediate: true } ) watch( inputValue, () => { emit('update:modelValue', parseFloat(inputValue.value.toString().replace(/(\d+),(?=\d+(\D|$))/g, '$1'))) }, { immediate: true } ) </script> <template> <input class="form-input" v-model.lazy="inputValue" v-cleave="{ numeral: true, numeralThousandsGroupStyle: 'thousand' }" :placeholder="props.placeholder" :required="props.required" :readonly="props.readonly" :disabled="props.disabled" /> </template>
解决方案
核心思路是直接利用Cleave实例的内置事件和API同步值,避免手动字符串处理的误差,同时实现实时更新:
方案1:手动初始化Cleave实例(推荐)
通过手动创建Cleave实例,利用其onValueChanged事件实时触发值更新,同时用setRawValue同步父组件传入的初始值。
修改后的代码:
<script setup lang="ts"> import { ref, watch, onMounted } from 'vue' import Cleave from 'cleave.js' const props = withDefaults(defineProps<{ modelValue: string | number type?: string required?: boolean readonly?: boolean disabled?: boolean placeholder?: string }>(), { type: 'text', required: false, readonly: false, disabled: false }) const inputRef = ref<HTMLInputElement | null>(null) const inputValue = ref(props.modelValue) const emit = defineEmits<{ (e: 'update:modelValue', value: string | number): void }>() // 同步父组件传入的modelValue到输入框 watch( () => props.modelValue, (newVal) => { if (inputRef.value?._cleave) { inputRef.value._cleave.setRawValue(newVal) } else { inputValue.value = newVal } }, { immediate: true } ) onMounted(() => { if (!inputRef.value) return // 手动初始化Cleave,配置小数位数保证和显示一致 new Cleave(inputRef.value, { numeral: true, numeralThousandsGroupStyle: 'thousand', numeralDecimalScale: 2, // 固定保留2位小数,匹配显示效果 onValueChanged: (event) => { // 移除千分位分隔符,转成数字格式 const processedValue = Number(event.target.value.replace(/,/g, '')) emit('update:modelValue', processedValue) // 若需要字符串格式,替换为:emit('update:modelValue', event.target.value.replace(/,/g, '')) } }) }) </script> <template> <input ref="inputRef" class="form-input" :value="inputValue" :placeholder="props.placeholder" :required="props.required" :readonly="props.readonly" :disabled="props.disabled" /> </template>
方案2:复用v-cleave指令
如果想保留v-cleave指令,可在组件挂载后获取指令创建的Cleave实例,添加事件监听:
<script setup lang="ts"> import { ref, watch, onMounted } from 'vue' const props = withDefaults(defineProps<{ modelValue: string | number type?: string required?: boolean readonly?: boolean disabled?: boolean placeholder?: string }>(), { type: 'text', required: false, readonly: false, disabled: false }) const inputRef = ref<HTMLInputElement | null>(null) const inputValue = ref(props.modelValue) const emit = defineEmits<{ (e: 'update:modelValue', value: string | number): void }>() watch( () => props.modelValue, (newVal) => { if (inputRef.value?._cleave) { inputRef.value._cleave.setRawValue(newVal) } else { inputValue.value = newVal } }, { immediate: true } ) onMounted(() => { const cleaveInstance = inputRef.value?._cleave if (!cleaveInstance) return // 覆盖Cleave的onValueChanged事件,实时同步值 cleaveInstance.options.onValueChanged = (event) => { const processedValue = Number(event.target.value.replace(/,/g, '')) emit('update:modelValue', processedValue) } }) </script> <template> <input ref="inputRef" class="form-input" :value="inputValue" v-cleave="{ numeral: true, numeralThousandsGroupStyle: 'thousand', numeralDecimalScale: 2 }" :placeholder="props.placeholder" :required="props.required" :readonly="props.readonly" :disabled="props.disabled" /> </template>
关键说明
numeralDecimalScale: 2:明确设置小数位数,确保输入1000.123时自动截断为2位,和显示的1,000.12完全匹配onValueChanged事件:Cleave内置的实时输入监听事件,每次输入内容变化都会触发,无需依赖v-model.lazysetRawValue:用于同步父组件传入的值,保证输入框显示正确的格式化内容
内容的提问来源于stack exchange,提问作者Kangkung Gamers
相关产品推荐
相关产品推荐

