使用事件通信 — LWC Events 完全指南

LWC 事件系统完整参考。涵盖事件创建与派发(CustomEvent/paginator 示例)、detail 传递数据(仅原始类型/拷贝模式)、三种监听方式(声明式/lwc:on 多重+动态/命令式+addEventListener 反模式)、事件重定向机制、四种传播配置(bubbles/composed 组合详解+完整示例代码)、内部事件与祖父通信模式、跨 DOM 通信(LMS vs pubsub)、最佳实践(detail 原则/传播选择/命名空间/全局唯一性)、lwc:on 动态监听器五大规则。...

📅 2026/7/19 ✍️ ponybai 🏷️ lwc, salesforce, events

使用事件通信

使用事件通信

LWC 派发标准 DOM 事件,也可创建和派发自定义事件。事件用于向(沿组件包含层次)通信——子组件通知父组件用户操作。向通信通过属性传递或方法调用。跨组件通信使用 Lightning Message Service。

强烈推荐使用 CustomEvent 接口——跨浏览器一致性更好、无需设置或模板代码、可通过 detail 属性传递任意数据。事件命名规则:不要用大写字母和空格、用下划线分隔单词、不要on 前缀(会导致 ononmessage 这样的混淆)。

创建和派发事件

创建和派发事件

CustomEvent() 构造函数创建事件,用 this.dispatchEvent() 派发。完整示例——分页器组件:

<!-- paginator.html -->
<lightning-button label="Previous" onclick={previousHandler}></lightning-button>
<lightning-button label="Next" onclick={nextHandler}></lightning-button>

// paginator.js
previousHandler() { this.dispatchEvent(new CustomEvent("previous")); }
nextHandler() { this.dispatchEvent(new CustomEvent("next")); }

父组件监听——HTML 属性语法 on{eventtype}

<c-paginator onprevious={previousHandler} onnext={nextHandler}></c-paginator>

page = 1;
previousHandler() { if (this.page > 1) this.page--; }
nextHandler() { this.page++; }

这些是简单的"某事发生了"事件——不传递数据载荷,仅声明按钮被点击。参考 lwc-recipes eventSimple + paginator

在事件中传递数据 —— detail 属性

传递数据

CustomEventdetail 属性向上传递数据。关键原则:仅发送原始类型——JavaScript 对非原始类型使用引用传递,任何监听器可以在组件不知情的情况下修改对象。如需要传对象,先拷贝到新对象再放入 detail。

// contactListItem.js —— 子组件派发选中事件
selectHandler(event) {
  event.preventDefault();
  const selectedEvent = new CustomEvent("selected", { detail: this.contact.Id });
  this.dispatchEvent(selectedEvent);
}

// eventWithData.js —— 父组件接收 ID 并查找对应联系人
contactSelected(event) {
  const contactId = event.detail;
  this.selectedContact = this.contacts.data.find(contact => contact.Id === contactId);
}

<c-contact-list-item contact={contact} onselected={contactSelected}>
最佳实践:不要将 @api 或 @wire 的非原始值直接放入 detail——这些值被只读膜包装,在跨 LWC-Aura 桥接时只读膜可能丢失导致数据被意外修改。参考 lwc-recipes eventWithData

处理事件 —— 声明式与 lwc:on

处理事件

声明式监听(推荐——减少代码量):模板中用 on{eventname} 属性。

lwc:on 多重监听:一个对象绑定多个事件处理器——属性键为事件类型字符串,值为处理器函数。处理器自动绑定到组件实例,this 可访问组件属性和方法:

eventHandlers = { click: this.handleClick, mouseover: this.handleMouseOver };
<lightning-button lwc:on={eventHandlers} label="Click me"></lightning-button>

lwc:on 动态切换:可通过重新赋值 eventHandlers 对象动态切换监听的事件类型——父组件在运行时决定监听 customEvent 还是 anotherCustomEvent

处理事件 —— 命令式与事件重定向

命令式与重定向

命令式监听(不推荐):在 JS 中用 addEventListener。两套语法:Shadow 边界内的元素用 this.template.addEventListener();非模板拥有的元素(如通过插槽传入的)用 this.addEventListener()。框架管理组件生命周期内监听器的清理——但如果添加到 windowdocument 等外部对象,你必须自己在 disconnectedCallback 中移除

反模式:不要用 addEventListener("event", this.handler.bind(this))——bind() 每次返回新函数,导致 removeEventListener 无法匹配,造成内存泄漏。用箭头函数类字段代替。

事件重定向(Event Retargeting)

事件冒泡跨越 Shadow 边界时,Event.target 的值改变以匹配监听器的作用域——保护 Shadow DOM 封装。例如 <my-button> 内的 <button> 被点击,外部监听器看到 Event.target === my-button,而非内部 button。在 todo-item 内部,target 是 div;在 todo-app 监听器中,target 是 c-todo-item

配置事件传播 —— bubbles 与 composed

事件传播配置

事件通过冒泡阶段传播(不支持捕获阶段)。两个布尔属性控制传播行为——默认均为 false

  • bubbles:是否通过 DOM 向上冒泡。默认 false
  • composed:是否能穿越 Shadow 边界。默认 false

三种实用配置组合(第四种 bubbles:false, composed:true LWC 不使用)。辅助属性:Event.target——派发事件的元素(受重定向影响);Event.currentTarget——当前处理器的附着元素(始终不变);Event.composedPath()——事件遍历路径的数组。

配置 1:bubbles:false, composed:false(推荐——默认)

默认配置

事件不冒泡、不穿越 Shadow 边界。只能通过直接在派发组件上附加监听器来捕获。事件仅到达 c-child 自身。处理器中 event.currentTarget = c-childevent.target = c-child

推荐此配置——破坏性最小、提供最佳封装。它不会强制消费组件和祖先组件将事件类型纳入其公共 API。参考 lwc-recipes 中 eventWithDatacontactListItem 就是使用此配置。

配置 2:bubbles:true, composed:false

bubbles:true

事件在Shadow 边界内冒泡,但不穿越边界。c-child 和 div.wrapper 都能响应。处理器中 c-child 的 target 是 c-child;div.wrapper 的 currentTarget 是 div.wrapper,target 是 c-child。

两种用途:① 创建内部事件:在模板中的元素上派发——仅冒泡到该模板内的祖先元素,到达 Shadow 边界即停止。外部父组件的处理器不执行。② 向祖父组件发送事件:如果组件通过插槽传入,在宿主元素上派发——事件仅在包含该组件的模板中可见。参考 lwc-recipes eventBubbling 组件。

配置 3:bubbles:true, composed:true

composed:true

事件冒泡穿越所有 Shadow 边界,一直传播到文档根节点重要警告:使用此配置 → 事件类型成为组件公共 API 的一部分,同时强制消费组件和所有祖先将事件纳入其 API。可能造成名称冲突——导致错误的监听器被触发。

如果必须使用:给事件类型加命名空间前缀(如 mydomain__myevent),但 HTML 监听器名会变成 onmydomain__myevent——很别扭。尽量避免。

配置 4 与跨 DOM 通信

跨 DOM 通信

bubbles:false, composed:true——LWC 不使用此配置。

跨 DOM 通信

不在同一 DOM 树中的组件间通信——首选 Lightning Message Servicelightning/messageService):① 声明消息通道(LightningMessageChannel 元数据);② 用 publish() 发布消息;③ 用 subscribe()/unsubscribe() 订阅。优势:不限于单页面、跨 LWC/Aura/Visualforce、跨标签页/弹出窗口、跨命名空间。

pubsub 模块(不推荐):仅在不支持 LMS 的容器中使用。限制于单页面,需手动注销所有注册组件。不是官方支持或主动维护的模块

事件最佳实践

事件最佳实践

detail 数据原则:

  • 同 Shadow 树通信 → 不要加 myProperty 到 detail——消费者可用 event.target.myProperty
  • 跨 Shadow 树通信 → 必须用 detail(event.target.* 不工作——真实目标不可见)
  • 始终用原始类型——非原始值会被任何监听器修改
  • 如必须传非原始值 → 拷贝到新对象再放入 detail
  • 不要将 @api 或 @wire 的非原始值直接放入 detail——只读膜可能在 IE11/跨桥接时丢失

传播配置原则:优先 bubbles:false, composed:false——破坏性最小。不要用 bubbling + composed——整个 DOM 树都会收到。如果用了 composed:true,事件类型应全局唯一

动态事件监听器 —— lwc:on 注意事项

lwc:on 注意事项

使用 lwc:on 的关键规则:

  • 不能修改传入 lwc:on 的对象引用的属性——必须传新对象来修改
  • 可以重新赋值变量到新对象——省略属性→移除对应监听器;新增属性→添加监听器;属性都存在→更新监听器
  • 推荐用 lwc:on 而非 lwc:spread 动态添加事件监听器
  • 同一事件类型同时使用 lwc:on 和 onevent 监听器会抛出错误
  • 同时用 lwc:on 和 lwc:spread 指定同一事件类型 → 两个监听器都会被添加

完整动态组件事件方案:lwc:component + lwc:is(加载) → lwc:spread(属性) → lwc:on(事件监听器)。

感谢阅读本指南。如需继续学习,请参阅下一章:使用 Salesforce 数据。