emit仅负责上报用户操作,过滤逻辑必须由父组件的computed响应式计算实现,否则会导致重复计算、状态割裂和联动断裂。

直接用 emit 实现规格联动过滤是常见误区——它只负责“通知”,不承担“计算”或“过滤”。真正起作用的是 computed 配合响应式状态,emit 仅在用户操作后把新选择透传给父组件,由父组件统一维护筛选逻辑。否则容易导致子组件各自为政、状态不一致、过滤失效。
为什么不能靠 emit 做过滤决策
emit 是事件通信机制,本质是“我点了这个,你看着办”。它不感知商品数据、不判断组合是否存在、也不更新可用规格列表。如果每个规格子组件都通过 emit('select', {type: 'color', value: '红色'}) 向上抛事件,再由父组件手动调用过滤函数,会带来三个实际问题:
- 重复计算:每次点击都触发全量
goodsList.filter(...),无缓存,性能下降明显 - 状态割裂:子组件不知道当前已选哪些规格,无法禁用无效选项,用户可能点出“无结果”组合
- 联动断裂:选完颜色后尺码没更新,需额外监听多个
emit并做防抖/合并处理,逻辑臃肿
emit 的正确定位:只做“动作上报”
把 emit 当作“操作日志”,而非“业务中枢”。子组件只需干净地告诉父组件:“用户刚选了什么”,其余交由父组件的响应式系统自动处理:
- 子组件中:
this.$emit('spec-change', { type: 'size', value: 'L' })(Vue 2)或emit('spec-change', { type: 'size', value: 'L' })(Vue 3 setup) - 父组件接收后,仅更新
selectedSpecs响应式对象:selectedSpecs.value = { ...selectedSpecs.value, size: 'L' } - 所有过滤逻辑(
availableSpecs、filteredGoods、matchedSku)全部由computed自动派生,无需手动调用
父子协作的关键设计点
要让 emit 发挥实效,必须配合以下结构设计:
-
扁平化规格数据:父组件提供
specs = { color: ['红','蓝'], size: ['S','M'] }和goodsList = [{ specMap: { color:'红',size:'S' } }, ...],子组件不持有原始商品数据 -
单向数据流约束:子组件只读
props.selectedSpecs和props.availableSpecs[type],不自行计算可用值 -
防错清空逻辑写在父组件:当
filteredGoods.length === 0时,父组件主动重置后续规格(如清空material),再通过v-model或props同步到子组件 -
禁用态由 availableSpecs 驱动:子组件渲染时,对
value不在availableSpecs[type]中的选项添加aria-disabled="true",不依赖 emit 判断
一个典型调用链示意
用户点击尺码“XL” → 子组件 emit('spec-change', {type:'size',value:'XL'}) → 父组件更新 selectedSpecs → 触发 computed availableSpecs 重新计算(此时 color 可选项自动收缩)→ 触发 computed filteredGoods 更新 → 子组件收到新的 availableSpecs props,自动刷新按钮状态 → 页面完成联动









