VueJS实现两位小数精度与平滑输入:解决双向监听抖动问题
Vue 双向监听输入抖动、光标跳转问题解决(不用Timeout,保留两位小数)
问题背景
用Vue的watch监听percentage和second实现联动时,输入百分比会触发第二个数值更新,反过来第二个数值更新又会触发百分比重新计算,形成循环导致输入抖动,光标还会跳到小数点末尾,要保留两位小数,又不想用Timeout来规避。
解决思路
- 打断循环更新:不要每次值变化就触发更新,判断计算出的新值和当前值是否有明显差异(比如差值超过0.001),避免浮点数精度误差触发无意义的循环。
- 精准控制小数位数:用
toFixed(2)格式化数值,但要转成数值类型,避免字符串绑定引发的异常。 - 保留光标位置:输入时记录光标位置,DOM更新后恢复光标,避免光标乱跳。
修改后的代码
<script setup> import { reactive, watch, nextTick } from 'vue' const data = reactive({ first: 50000, second: 20000, percentage: '' }) // 监听百分比,更新第二个数值 watch(() => data.percentage, (newValue) => { const numPercent = parseFloat(newValue) if (isNaN(numPercent)) return const calcSecond = (data.first * numPercent) / 100 // 只有差异大于0.001才更新,避免循环触发 if (Math.abs(calcSecond - data.second) > 0.001) { data.second = Number(calcSecond.toFixed(2)) } }) // 监听第二个数值,更新百分比 watch(() => data.second, (newValue) => { const numSecond = parseFloat(newValue) if (isNaN(numSecond)) return const calcPercent = (numSecond / data.first) * 100 // 同样判断差异,避免循环 if (Math.abs(calcPercent - parseFloat(data.percentage)) > 0.001) { data.percentage = calcPercent.toFixed(2) } }) // 自定义指令:保存并恢复光标位置 const vPreserveCursor = { mounted(el) { el.addEventListener('input', function() { this._cursorPos = this.selectionStart }) }, updated(el) { nextTick(() => { if (el._cursorPos !== undefined) { el.setSelectionRange(el._cursorPos, el._cursorPos) } }) } } </script> <template> <div> <div> <label>First</label> <input type="number" v-model.number="data.first" step="1"> </div> <div style="padding: 15px 0px"> <label>Second</label> <input type="text" v-model="data.second" v-preserve-cursor> </div> <div> <label>Percentage</label> <input type="text" v-model="data.percentage" v-preserve-cursor> </div> </div> </template>
关键改动说明
- 循环打断:每个watch回调中增加差值判断,只有当计算值与当前值的差异超过0.001时才执行更新,从根源上避免了双向触发的循环,自然消除了抖动现象。
- 小数精度处理:用
toFixed(2)将数值固定为两位小数,再通过Number()转换为数值类型,避免字符串绑定带来的格式异常。 - 光标修复:自定义
v-preserve-cursor指令在用户输入时记录光标位置,DOM更新完成后通过nextTick恢复光标,保证输入时光标不会跳至末尾。 - 输入优化:将First输入框改为
type="number",搭配v-model.number确保输入为纯数值类型,减少非数字输入的处理逻辑。
补充说明
如果想用computed属性实现,也可以将second和percentage设置为带get/set的计算属性,核心思路还是通过差异判断避免循环更新,本质和上述方案一致。
内容的提问来源于stack exchange,提问作者shabxs
相关产品推荐
相关产品推荐

