必须每次点击重算所有规格可用状态,后端skus需带规范spec_value_ids字符串,specs与skus严格分层,禁用逻辑基于当前已选条件动态过滤sku并提取关联值,选中规格变更时需按序清除后续已选项。

直接说结论:不能靠“选完颜色再筛尺码”这种线性逻辑,必须每次点击都重算所有规格的可用状态;后端返回的 skus 必须带明确的 spec_value_ids 字符串(如 "color:red|size:m"),否则前端硬解析必翻车。
specs 和 skus 必须严格分层,混在一起就崩
常见错误是把规格定义和组合库存塞进一个数组里,比如用二维数组模拟“颜色×尺码”,结果一加型号就爆炸。真实业务里“红+S+金属壳”和“红+S+塑料壳”库存可能完全不同,二维结构根本表达不了。
-
specs只负责描述维度:[{name: "颜色", values: ["红","蓝"]}, {name: "尺寸", values: ["S","M"}] —— 不含任何库存信息 -
skus只存合法组合:[{id: "1001", spec_value_ids: "color:red|size:s", stock: 5, price: 99.9}, ...] —— 每个spec_value_ids必须是后端约定好的唯一字符串,不能靠前端拼接顺序推断 - 如果后端只给一个扁平
sku_list数组,且spec_str字段是类似"红色,S"这种无字段标识的字符串,立刻提需求补字段。靠 split(",") 硬拆会挂掉——比如“深灰,XXL”和“浅灰,XXL”在数组里位置一变,匹配就错
点击一个规格值时,禁用逻辑怎么写才对
不是“当前已选颜色下找所有尺码”,而是“找出所有包含这个颜色的 SKU,再看这些 SKU 里共出现了哪些尺码值,其他尺码全部禁用”。这个计算必须每次点击都跑一遍,缓存中间状态会漏判。
- 先生成当前已选 key,比如
"color:red" - 过滤
skus.filter(sku => sku.spec_value_ids.includes("color:red")) - 对每个未选规格(如
"size"),遍历这些 SKU 的spec_value_ids,提取所有匹配size:xxx的值,得到可用集合["s", "m"] - 把
specs中该规格的values映射成带disabled字段的新数组,没出现在可用集合里的就设true -
uni-app 的
v-for绑定原数组不会响应式更新disabled,必须用computed或watch生成新数组,否则按钮点不动
uni-app 里 handleSpecClick 怎么避免状态污染
用户点“颜色-蓝”后又点“尺寸-M”,这时“颜色-红”不该还保持选中状态——后续规格变化必须清空更靠后的已选项,否则 selectedSpecs 会残留无效键值对,导致禁用计算出错。
- 用
allSpecNames = this.specList.map(s => s.name)获取规格顺序 - 拿到当前点击的
specName在allSpecNames中的索引currentIndex - 遍历
Object.keys(this.selectedSpecs),对每个name查其索引,若大于currentIndex就用this.$delete(this.selectedSpecs, name)删除 - 别用
this.selectedSpecs = {...}全量替换——会丢失响应式,$delete是安全写法 - 价格、图片、库存显示必须和
selectedSpecs联动,但不要在watch里直接查skus数组——用find效率低,换成for循环提前 break
为什么 onReachBottom 会干扰 SKU 选择器
这不是 SKU 本身的问题,但实际项目里商品详情页常嵌套滚动列表(比如评价、推荐商品),onReachBottom 频繁触发会导致 selectedSpecs 被意外重置或请求重复发送。
- 根本原因是某些安卓 WebView 下
onReachBottom会连续触发多次,尤其页面高度动态变化时(比如弹出 SKU 选择器后) - 加节流:用
setTimeout+ 标志位,500ms 内只执行一次 - 更稳妥的是换用
scroll-view的@scrolltoupper事件,绑定在具体容器上,比页面级钩子可控 - 别依赖
uni-app自带的防抖——它只控制触发频率,不阻止你代码里重复调getSkus()这类副作用
最易被忽略的点:后端返回的 spec_value_ids 字符串格式必须全局统一,前端不能自己拼。哪怕只是多一个空格、大小写不一致,includes() 就失效——这种 bug 在真机上极难复现,上线后用户反馈“点了颜色没反应”,查半天发现是后端返回了 "Color:red"。










