vue 事件冒泡由浏览器原生机制执行,vnode 仅通过 patchprops 控制监听器的绑定、更新与清理;onxxx 字段仅声明意图,是否生效取决于响应式状态及 patchprops 的实际操作。

Vue 渲染器处理事件冒泡,并不依赖 VNode 主动“携带”冒泡行为,而是完全交由浏览器原生机制执行;VNode 的作用是决定“该不该绑定监听器”,以及“绑定到哪个节点”。冒泡本身是 DOM 自然发生的,VNode 只控制监听器是否存在、是否被更新、是否被清理。
事件绑定由 patchProps 决定,而非 VNode 结构
VNode 中的 onXxx 字段(如 onClick)只是声明意图,真正绑定与否,取决于 patchProps 函数在挂载或更新时的判断:
- 首次挂载时,若 props 包含
onClick,patchProps调用addEventListener('click', handler),监听器才真实存在 - 若某次更新中
onClick消失(比如响应式值变为null或undefined),patchProps会主动调用removeEventListener清理监听器 - 即使父节点 VNode 描述了
onClick,但若当前未生效(如条件未满足),它就**没有监听器**——冒泡到它身上也不会触发任何逻辑
冒泡路径与 VNode 树结构一致,但不依赖 VNode 主动参与
浏览器按真实 DOM 树向上冒泡,而 Vue 的 VNode 树结构通常与 DOM 树一一对应。这意味着:
使用 `@ainative/vue-sdk` 为 Vue 3 应用添加 AI 聊天功能。适用于以下场景:(1) 安装 `@ainative/vue-sdk`,(2) 在 Vue 组件中使用 `useChat` 组合式函数,(3) 提供……
- 点击子节点 VNode 对应的真实元素后,事件会逐层向上触发父级真实 DOM 元素上的监听器
- 但只有那些**当前已绑定监听器的父节点**才会响应——VNode 是否描述了事件、是否在本次更新中保留该事件,直接决定它能否“接收到”冒泡
- VNode 本身不拦截、不转发、不模拟冒泡;它只通过
patchProps控制监听器的生命周期
响应式更新可能“延迟生效”,造成冒泡误判
常见误解:点击子元素后,父元素事件突然执行,以为是“冒泡触发了未绑定的监听器”。实际是:
- 子元素点击触发 handler → 修改响应式状态 → 触发副作用重新执行 →
renderer.render生成新 VNode - 新 VNode 中父节点的
onClick已存在 →patchProps在更新阶段为其绑定监听器 - 这个绑定发生在冒泡传播过程中(或紧随其后),用户感知为“点击子元素,父节点立刻响应”,其实是**更新完成后的监听器已就位,下次冒泡才有效**;但因 JS 执行快,视觉上几乎同步
自定义事件与原生冒泡互不干扰
VNode 中的 onXxx 默认按原生事件处理(如 onClick → click),但组件 emits 声明会提前过滤:
- 若组件声明
emits: ['submit'],则onSubmit不会传给根 DOM,不会进入patchProps绑定流程 - 未声明的事件(如
onScroll)则视为原生事件,交由patchProps处理并绑定到根元素,参与正常冒泡 - 使用
v-bind="$attrs"+inheritAttrs: false可显式将未声明事件透传给根元素,使其进入冒泡链










