uni-app 中不能用过滤器实现可靠百分比转换,因 Vue 3 已移除 filters,且小程序平台不支持;应使用 computed 封装逻辑,处理 total 为 0、浮点误差等问题,并通过 :style 绑定宽度。

uni-app 里不能用过滤器实现可靠的百分比转换,尤其是 Vue 3(即 @vue/composition-api 或 setup)项目中,filters 已被移除;即使 Vue 2 模式下写好了 Vue.filter('percent'),小程序端(微信、支付宝、字节)也基本不执行——不是你写错了,是平台根本不支持。
为什么不能在 template 里用 {{ value | percent }} 做百分比
过滤器在 uni-app 3.x+ 中已被废弃,H5 端可能偶然生效,但真机调试时微信小程序基础库(2.19.0+)、支付宝(10.2.80+)等均忽略 filters 定义。更隐蔽的问题是:模板里直接写 {{ (voteCounts.a / total) * 100 | toFixed(1) }}% 会因 total === 0 导致 NaN,且 toFixed(1) 返回字符串,后续做 v-if 判断或数值比较时出错(比如 v-if="ratio > 50" 实际在比字符串和数字)。
正确做法:用计算属性或 setup 函数封装百分比逻辑
把百分比计算收口到响应式数据层,避免模板里拼接、判断、格式化三件事混在一起:
-
total为 0 时必须显式返回0,不能靠NaN || 0——某些小程序 JS 引擎对NaN > 0的行为不一致 - 乘 100 后立即用
Math.round(num * 10) / 10替代toFixed(1),规避浮点误差(如0.1 + 0.2→0.30000000000000004) - 如果要带 % 符号,建议只在 template 中加,不要塞进计算值里(方便后续做宽度、排序、导出等)
示例(setup 写法):
const voteRatios = computed(() => ({
'option-a': total.value === 0 ? 0 : Math.round((voteCounts.value.a / total.value) * 1000) / 10,
'option-b': total.value === 0 ? 0 : Math.round((voteCounts.value.b / total.value) * 1000) / 10
}))模板中用:<view>{{ voteRatios.value['option-a'] }}%</view>
百分比条宽度必须用 :style 绑定,别信 v-bind:width
v-bind:width 不是合法 HTML 属性,uni-app 编译后会被忽略。正确写法是:
<view :style="{ width: voteRatios.value['option-a'] + '%' }"></view>
注意三点:
- 别漏掉
%单位,否则宽度为 0 - 确保
voteRatios是响应式对象(用computed或ref包裹),否则首次渲染后不会更新 - 若需限制最大宽度(如不超过 100%),得额外判断:
Math.min(voteRatios.value['option-a'], 100)
真正容易被忽略的点是:百分比计算必须和数据更新时机强绑定——比如投票数通过 API 更新后,total 和各选项计数要同步变更,否则 computed 依赖没触发,视图就不刷新。别指望靠 watch 手动调用,那会破坏响应式链路。


















