Emit 的核心作用是子组件向父组件传递勾选状态变更,实现单向数据流:子组件计算新数组后通过 emit('update:modelValue', newArray) 通知父组件更新,父组件统一处理联动逻辑并重新下发。

树形结构中 Emit 的核心作用
在 Vue 树形组件(如自定义递归树或 Element Plus Tree)中,子节点勾选状态不能直接修改父组件数据,必须通过 emit 向上抛出变化。这是单向数据流的强制要求:父组件统一维护全量选中状态(如 checkedKeys 数组),子组件只负责“计算本次点击带来的新数组”,然后触发事件通知父组件更新。
勾选状态向上同步的关键步骤
每次点击一个节点时,子组件需完成三件事:
- 基于当前接收的
modelValue(即父传来的已选 ID 数组)判断该节点是否已选 - 生成新数组:已选则过滤掉,未选则追加;必须用新数组,不可 push/splice 原数组
- 调用
emit('update:modelValue', newArray),让父组件接收并替换旧状态
递归通信中的父子联动逻辑
树是嵌套结构,单个节点的勾选常需影响其父级或子级。但 emit 不递归、不跨层,它只把本层变化告诉直接父组件。真正的“联动”由父组件统一调度:
- 父组件收到子组件 emit 的新数组后,先更新自身
checkedKeys - 再调用辅助函数(如
getCheckedWithParents)遍历整棵树,补全所有被间接影响的父级 ID - 最后将合并后的完整数组重新下发给所有子组件(通过 props 更新)
例如:勾选叶子节点 111,父组件收到 [111] → 扫描树结构发现其父为 11、祖父为 1 → 合并为 [1, 11, 111] → 全局重置 modelValue。
避免常见陷阱的写法示例
以下是在 Vue 3 中安全处理勾选并 emit 的典型模式(适用于递归子组件):
- 子组件 props 声明:
modelValue: { type: Array, default: () => [] } - 点击处理逻辑:不直接操作 this.modelValue,而是:
const toggleNode = (id) => {<br> const current = [...props.modelValue];<br> const exists = current.includes(id);<br> const next = exists<br> ? current.filter(v => v !== id)<br> : [...current, id];<br> emit('update:modelValue', next);<br>};
这样既保证响应式生效,又为父组件留出扩展空间(比如加入父子联动、半选态计算等)。

















