Communicate with Events
同学们,今天我们来聊聊 Lightning Web Components 里的事件机制。这部分特别重要,因为组件之间怎么通信,就靠它了。大家可以把事件想象成在组件树里向上冒泡的一个信号,就像小时候玩的传话游戏,从下往上传递信息,而数据向下流则是通过属性,也就是我们常说的“props down, events up”。 首先,LWC 的事件是建立在标准 DOM 事件系统之上的,这让我们用起来很自然。但咱们推荐使用 `CustomEvent` 这个接口来创建事件。为什么呢?因为它跨浏览器表现一致,几乎不需要额外设置,而且能通过 `detail` 属性方便地携带数据。你想想,如果你自己造轮子,还得处理各种浏览器差异,多麻烦。 好,我们来一步步看事件的生命周期。 ,第一步,创建事件。, 很简单,用 `new CustomEvent` 构造函数就行。比如: ```js const myEvent = new CustomEvent('my-event', { detail: { message: '你好' } }); ``` 这里要注意命名约定:事件名称要使用全小写字母加连字符,不要用驼峰,更不要加“on”前缀。因为 HTML 模板里监听时,我们会用 `on` 开头,如果你事件名前面又加一个 `on`,就成 `onon...` 了,会很混乱。所以,事件名叫 `my-event`,监听时写 `onmyevent`。记住,事件名里的大写字母会自动转成小写,所以别紧张。 ,第二步,调度事件。, 用 `this.dispatchEvent(myEvent);` 就能把事件发出去。它会沿着组件层级往上冒泡,父组件就能收到。 ,第三步,处理事件。, 在父组件的模板里,咱们可以声明式地监听,就像这样: ```html <c-child onmyevent={handleMyEvent}></c-child> ``` 只要子组件派发了 `my-event`,父组件的 `handleMyEvent` 函数就会被调用。而且,你还可以用 `lwc:on` 指令来动态绑定监听器,或者一次绑定多个事件处理器,非常灵活。比如: ```html <template lwc:onmyevent={handleOne} lwc:onmyevent={handleTwo}> <!-- 内容 --> </template> ``` 这样同一个事件可以触发多个处理函数。当然,我们也能用命令式的方法,在 JavaScript 里通过 `addEventListener` 直接监听某个元素,比如 `this.template.addEventListener('my-event', this.handleMyEvent.bind(this))`,但别忘了在组件销毁时用 `removeEventListener` 清理,防止内存泄漏。 接下来,我们谈谈事件传播。默认情况下,LWC 里的事件只在组件模板的阴影边界内冒泡,不会穿透 Shadow DOM。如果你想让事件穿透影子边界,必须显式设置 `composed: true`,同时还要搭配 `bubbles: true`,这样事件才能一路向上,穿过各个影子根,到达最外层。就像这样: ```js new CustomEvent('my-event', { detail: { ... }, bubbles: true, composed: true }); ``` 但是要注意,合成事件(composed events)可能会接触到不属于你组件的 DOM 范围,所以要小心数据泄露或者意外触发外部监听器。 说到跨 DOM 通信,有时候你的组件不在同一个 DOM 树里,比如在闪电页面里放了好几个独立组件,这时冒泡机制就不行了。Salesforce 提供了 ,Lightning Message Service (LMS),,允许你在任意组件之间通过发布/订阅模式传递消息,无论它们在页面的什么位置,甚至跨框架。LMS 使用 `MessageContext` 和 `publish`、`subscribe` 等方法,这是专门用来解决跨组件强耦合问题的。 最后,我给大家总结一些最佳实践: - 始终优先使用 `CustomEvent` 和使用 `detail` 传数据。 - 事件名用小写字母加连字符,保持一致性。 - 谨慎使用 `bubbles: true` 和 `composed: true`,只在确实需要穿透边界时才开启,否则就在组件内部处理。 - 尽量用声明式监听 `on...`,它更清晰,也更符合 LWC 的设计哲学。只有在处理第三方库或特殊需求时,才用命令式监听。 - 记得清除监听器,避免内存问题。 - 跨组件通信,如果组件之间没有直接的父子关系,考虑使用 LMS 而不是层层传递事件。 好了,今天的事件系统就讲到这里。大家只要记住核心思想:事件向上冒泡,携带数据;谨慎配置传播;灵活选用声明式和命令式;跨作用域用 LMS。动手写几个小例子,很快就熟悉了。有疑问随时问!
本课程共有 11 个章节
同学们,咱们今天要讲Lightning Web Components里的事件创建和命名。想象一下,子组件想告诉父组件“嘿,我这里有事情发生”,就需要派发一个事件。创建事件有个固定的模式:用`CustomEvent`构造函数,给它一个字符串类型的事件名,然后调用`dispatchEvent()`把这个事件派发出去。 命名这里有三个规则,一定要注意:第一,不用小写字母,这是DOM的约定,事件名是区分大小写的,我们通常用驼峰式,比如`previous`;第二,别在里面加空格;第三,单词之间用下划线分隔,比如可以用`page_change`这种。特别强调一下,千万不要在事件名前面加“on”前缀。为什么?因为如果你的事件名叫`onmessage`,父组件监听时,模板里属性会变成`ononmessage`,两个“on”叠在一起,就乱套了,容易让人头疼。 接下来我们看个最简单的例子——分页器组件。它是一个可重用的子组件,里面有“上一页”和“下一页”两个按钮。点击“上一页”,子组件就派发一个叫`previous`的事件;点击“下一页”,派发`next`事件。父组件呢,在模板里用`onprevious`和`onnext`属性来监听,注意这里“on”是小写,后面接事件名的首字母大写。父组件的处理函数收到事件后,就把页码计数器加减。这些事件非常简单,就是纯粹的通知,不携带任何数据,只是告诉父组件“俺被点了”。明白了没?这种模式在开发里面特别常用,大家要记牢。
同学们,咱们今天来讲一个在 Lightning Web Components 里非常重要的概念,就是事件里的 `detail` 属性。你可能会问,它是什么?简单来说,它就是子组件向父组件传递数据时用的一个“包裹”。 打个比方,你在联系人列表里点击了某个人,子组件要告诉父组件“我点了谁”。这时候,子组件就会触发一个自定义事件,然后把数据塞进 `detail` 里。 这里有一个最佳实践,特别重要,大家一定要记住。子组件在 `detail` 里最好只传一些基础的、简单的值,比如一个联系人 ID。千万别把整个联系人对象一股脑传上去。 为啥呢?因为如果传的是对象,那本质上你传的是这个对象的“引用”。也就是说,收到这个事件的任何一个监听器,都有可能直接去修改这个对象,这就会引发各种莫名其妙的问题,数据可能就乱了。 所以,建议很明确:在 `detail` 里,只放字符串、数字这种基本类型,或者如果你一定要传对象,那就先把它深拷贝一份,传拷贝过去。反正,绝对不能把原始对象直接丢进去。 那么,父组件拿到这个事件后,怎么处理呢?它从 `event.detail` 里拿到这个 ID,然后去它自己手里掌握的那份数据里查找,找出完整的联系人信息。这样一来,子组件只需要知道自己负责的那一小块 ID,而父组件拥有完整的数据和掌控权。这种模式让组件之间的约定非常干净,职责分明,代码也更好维护。 记住,`detail` 就像一条清晰的送货单,上面只写必要的编码,而具体的货物,父级手里本来就有。好,这块就讲到这里,大家理解了吗?
好,同学们,我们来看一下声明式事件处理。 在 Lightning Web Components 中,处理事件的首选方式就是声明式的。为什么?因为它更干净、代码更少。你不用再手动调用 `addEventListener` 了,直接在模板里用 HTML 属性来绑定事件就行。 具体怎么做呢?很简单,属性名以 `on` 开头,后面跟着事件类型。比如,要监听点击事件,就写 `onclick={handleClick}`。这样一来,LWC 就会自动帮你把事件处理函数绑定到元素上。 那如果我们想要更灵活一点呢?比如需要绑定多个事件监听器,或者事件类型是动态确定的。这时候就不能用固定的 `onclick` 这种形式了。LWC 提供了一个特殊的指令 `lwc:on`,它能接受一个对象。这个对象的键是事件类型字符串,值就是对应的处理函数。 举个例子,你可以这样写:`<div lwc:on={eventHandlers}></div>`。然后在 JS 里,`eventHandlers` 是一个对象,比如 `{ click: handleClick, mouseover: handleMouseOver }`。LWC 会自动把这个对象里的每个事件都绑定好。 而且,和原生 `addEventListener` 不一样的是,`lwc:on` 自动把处理函数绑定到了组件实例上。也就是说,你在处理函数里面用 `this`,它指向的就是你的组件,不会跑丢,非常方便。 好,重点来了,动态监听器怎么更新?你可以随时给 `eventHandlers` 重新赋一个新对象,来添加、删除或者修改事件监听器。但是要注意,你不能直接修改现有对象的属性,比如 `this.eventHandlers.click = newHandler`,这样会报错。你必须给它一个全新的对象引用。 那规则是怎样的呢?很简单: - 从新对象里少了某个属性,对应的监听器就被删除。 - 添加了新的属性,就新增一个监听器。 - 保留的属性如果值变了,就会更新监听器。 最后,在组件的生命周期里,比如 `linkedCallback` 或者按钮点击时,子组件会派发事件,这时候你的声明式监听器就会按预期触发。 记住这些,事件处理就能写得又简洁又好维护。
同学们好,今天咱们来讲讲Lightning Web Components里事件处理的几个关键要点,尤其是强制事件处理时,怎么正确使用addEventListener。这里有两个语法,很多同学容易搞混,我给大家理一下。 第一个是 `this.template.addEventListener()`,这个用在你的阴影边界内的元素上,也就是你组件模板里自己定义的那些元素。比如模板里有个按钮,你想监听它的点击事件,就用这个。 第二个是 `this.addEventListener()`,它用在你的模板不拥有的元素上,最典型的就是通过插槽分发进来的内容,也就是常说的slot content。因为这些元素的所有权不在你模板内部,所以要挂载在组件宿主元素上。 接下来,我要敲黑板说一个反模式:千万不要把 `.bind(this)` 和 addEventListener 一起用。为什么呢?因为 bind 方法每次调用都会创建一个全新的函数实例。这样一来,你之后用 removeEventListener 移除监听的时候,因为找不到刚才那个一模一样的函数引用,就移不掉了,监听器会一直留在内存里,造成内存泄漏。正确的做法是用箭头函数,箭头函数能从词法作用域上直接捕获 this,没有反复创建新实例的问题,代码又干净又安全。 第三个重点叫事件重定向,这是影子DOM里一个非常重要的概念。当一个事件从你的阴影内部冒泡,穿过阴影边界到达外部时,框架会自动把 event.target 的值修改为你的组件宿主元素。这么做是为了保护内部DOM结构,不让外界直接窥探到你里面的细节。所以你在外部事件处理函数里拿到的 target,永远是这个自定义元素本身,而不是里面那一个具体的按钮。 再强调一件关于输入字段的事:如果你的组件里用了 input 之类可以交互的标签,千万记得把组件属性跟 DOM 属性值始终同步。为什么呢?因为 LWC 的模板重新水化过程会检查属性和 DOM 的实际值是不是一致,如果不匹配,就可能出现莫名其妙的状态问题。所以,value 变了,就去更新对应的属性,反之亦然。 最后聊聊监听器的清理。框架很贴心地会自动帮你把挂载在模板元素上的监听器清理掉,不用你操心。但是,那些手动加在 window 或者 document 上的监听器,框架就管不着了。你必须在 disconnectedCallback 生命周期里,手动调用 removeEventListener 把它们逐一移除,否则组件销毁了,那些监听器还赖在那儿,久而久之内存就涨上去了。 好了,把这几条记牢,你的事件处理代码就能既健壮又优雅。
同学们,今天我们来说一下LWC里的事件传播,这和我们熟悉的普通网页DOM事件很相似,只是多了些Web Component特有的细节。先理解两个最关键的属性:“bubbles”和“composed”。 “bubbles”就是冒泡,它决定事件会不会像水里的气泡一样,从触发元素一直向上传到它的祖先元素。比如你在一个按钮上点一下,如果事件设置了bubbles为true,那这个按钮的父元素、祖父元素、一直到根元素都能监听到。要是设为false,事件就停在原地,不向上跑。 第二个属性是“composed”,它控制事件能不能穿透“影子边界”。LWC里的每个组件都有自己的影子DOM,外面是看不到里面的。默认情况下,事件只在自己的影子DOM里冒泡,传到影子根就停了。但如果你把composed设为true,事件就能穿过这个边界,继续向上冒泡到父组件,这就让跨组件的事件通信成为可能。 要注意,LWC只支持事件传播的“冒泡阶段”,它不支持“捕获阶段”。也就是说,你没法在父元素上先用捕获的方式拦截事件,事件只能从目标元素开始一层层向上冒泡。这是LWC在性能和安全上的一个刻意设计。 了解这两个属性后,我们来看看事件处理时有三个好用的属性帮你了解传播路径。 第一个是“target”,它指向事件最初调度出来的那个元素,也就是最开始触发事件的源头。但在影子DOM里,这个target会被“重新定位”。什么意思呢?就是如果事件从组件内部的一个按钮发出,当它穿过影子边界后,在外层组件眼里,target就变成了那个自定义元素标签,比如c-child,而不是内部的button。这样保护了组件内部结构,也符合Web组件的封装原则。 第二个是“currentTarget”,它指的是当前正在处理这个事件的那个元素。随着事件向上冒泡,currentTarget会跟着变。比如你在c-parent上挂了个事件处理器,当事件冒泡到c-parent时,那个处理器里的currentTarget就是c-parent。而在子组件里,它可能是c-child。所以currentTarget答案“事件现在走到谁家了”。 第三个是“composedPath()”方法,它能返回一个完整的数组,记录了事件传播路径上所有经过的目标元素,包括那些被影子边界重新定位前的真实内部节点。这在调试时非常有用,可以看到事件到底穿过了哪些组件。 我们拿一个静态的合成示例来说明:假设有个最外层的c-app组件,里面放了c-parent组件;c-parent内部有一个div包裹着c-child组件和一个原生按钮。我们对每个级别都挂上事件监听器。用一张“扁平树”图去看,就会发现: - 当你点按钮时,事件如果设置了bubbles true和composed true,它在c-child内部先冒泡,然后穿过c-child的影子边界,再经过包裹的div,再到c-parent,最后到达c-app。 - 在每个级别,target在组件内部可能显示具体按钮,但过了边界就显示c-child,而currentTarget跟着处理器的附着点变化。 - composedPath()把这些节点全都记录下来,你可以看到一条完整的路径。 总结一下,掌握LWC的事件传播,记住这两句口诀:bubbles管向上冒泡,composed管跨边界。只冒泡不捕获,target会重定位,currentTarget随冒泡而变,composedPath给全貌。搞懂这些,你就能轻松驾驭组件间的事件通信了。
来,同学,今天咱们聊聊 Lightning Web Components 里事件传播的一个特别省心的默认配置。你把它记住,能省掉不少麻烦。 想象一下,你是一个组件作者,你只想让爸爸妈妈知道你做了啥,不想让爷爷、太爷爷也知道。这时候,咱们就可以用 LWC 默认给你准备好的传播方式:,bubble 设为 true,composite 设为 false,。 这几个参数是干嘛的呢?简单说,bubble 控制了事件会不会像水泡一样往上面冒,composite 决定了它能不能穿透那个叫“阴影边界”的东西,去影响影子世界的外面。默认的设置就是:会冒,但,冒不出你自己的那个影子边界,。 所以,只有那个直接把你的组件包起来的“直接父组件”,才能监听到这个事件。爷爷、太爷爷,统统听不到。这就让这个事件变成了你和爸爸之间的小秘密,爸爸不用跟别人解释,别人也不用知道你到底干了啥——因为它成了一种实现细节,而不是一个公开的 API。 你试着在“c-child”组件上面放一个事件处理,然后在处理函数里打印一下 event.currentTarget 和 event.target,你会发现它们都指向了同一个东西:就是这个 c-child 宿主元素。事件没往更深或更广的地方去。 有一个很好的例子,就是 lwc-recipes 里的 c-contract-list-entry 组件。它内部会调度一个叫“selected”的事件,用上了这个默认配置。结果呢,只有它的直系父级,也就是列表容器,能听见这个事件,其他外面的组件根本感知不到。这种隔离开的设计,让内部修改特别自由,不会不小心把外面的世界弄乱。 所以,老师给你的建议是:,每次设计事件,先从这个默认配置开始,。这是破坏性最小的,也是最安全的选择。什么时候你要改动它呢?只有当你真的有一个明确的需求——比如非得让祖辈组件知道这件事,或者必须让它能穿过影子边界往外传——这时候你再考虑把 bubble 或 composite 的值加大。别一上来就加,先默认,再按需调整,你的组件就能既干净又好维护。 记住这个小原则,你写组件会越来越有章法。好,咱们就讲到这儿,有什么问题随时问。
同学们,今天我们来看一下 Lightning Web Components 里事件冒泡的一种常见配置,也就是 ,bubbles 设为 true,但 composed 设为 false, 的情况。 先别被参数吓到,我们可以把它想象成“在房间里喊话”。,bubbles: true, 表示这个事件会向上冒泡,就像声音在房间里能传开;而 ,composed: false, 就好比房间的墙是完全隔音的,声音到墙边就停了,外面的父组件根本听不到。 具体来说,当一个事件在组件的影子 DOM 树内部触发时,它会沿着这个树一直向上冒泡,直到碰到影子边界——也就是自己这个组件的“墙”。然后它就停在那里,不会跑到外层的父组件那里去。 举个具体的例子:假设我们有一个 `c-child` 组件,它模板里有个 `<div class="wrapper">` 包裹了一些内容。事件在某个子元素
同学,我们今天讲一个比较重要的配置:事件冒泡的`bubble: true` 和 `composite: true` 同时开启的情况。这听起来有点技术,但你只要记住一点:,这是权限最大、也是代价最高的组合,。 我们想象一下,页面是由一层一层组件嵌套起来的,就像俄罗斯套娃,每个组件也可能有自己的阴影边界,类似一个封闭的小世界。普通的冒泡事件可能只在自己这一层里面传,但如果同时把`bubble`和`composite`都设成`true`,这个事件就会变得特别猛——它能穿过DOM的每一层,穿过每一个阴影边界,一直冲到整棵文档树的根部。也就是说,它路径上的每一个祖先组件,无论隔了多少层,只要它愿意,都能听到这个事件。 那这带来什么问题呢? 首先,你的事件类型突然就变成了整个组件公共API的一部分。因为路径上任何一个祖先组件都可以去监听它,你就等于和每一个祖先组件都签了一个无形的合同:只要我发了这个事件,你们都有权处理。这个合同的“签名”范围太大了,以后你想改事件名字、改事件内容,都会牵连一大片,维护成本非常高。 更麻烦的是,,名称冲突,会变成一个真实的危险。如果两个完全无关的组件都用这个配置,而且碰巧发了同名的事件,比如都叫`itemselected`,那某个祖先组件可能就会错误地触发监听器,它分不清到底是哪个后代发来的。这就是典型的“说者无心,听者错意”。 如果迫不得已必须用这种模式,一定得给你的事件类型加个,命名空间前缀,,像`mydomain__myevent`这种格式。虽然写出来有点丑,监听器的HTML属性会变成`onmydomain__myevent`,但至少能保证全局唯一,不会跟别人撞名字。 总的来说,你要记住,避免同时用`bubble: true`和`composite: true`,除非你有一个非常明确、文档写得清清楚楚的需求,就是想让事件跑到遥远的祖先那里去。否则,这就像在小区里用大喇叭喊话,整栋楼都听见了,很可能吵到不该吵的人,还把自己绑在一堆承诺上。 好了,这个配置的影响我们就讲清楚了,下一步可以看看更安全的事件通信方式。
同学们,我们来看一下LWC里事件传播的配置。首先记住一个要点:千万不要把 bubbles 和 composed 同时设为 true,也就是不要用第四种组合。 那怎么选择呢?很简单,从默认值开始,默认两个都是 false。如果你的事件只是在组件内部,或者要发给直接插槽里的内容,只需要把 bubbles 设为 true。只有在绝对必要的时候,才把 composed 也设为 true,并且最好给事件名加上命名空间前缀,避免冲突。 不过,对于真正需要跨不同 DOM 树、甚至跨页面通信的场景,我们不靠这些配置硬撑,而是推荐用 Lightning Message Service,简称 LMS。 LMS 非常强大,能跨页面工作,支持 LWC、Aura、Visualforce,还能跨选项卡、弹出窗口,甚至跨命名空间。实现起来也不复杂,分三步走:第一,声明一个消息通道;第二,发布消息;第三,订阅和取消订阅。 最后提醒一下,旧版的 pubSub 模块虽然还在,但只适用于不支持 LMS 的容器,而且它只能在单个页面内通信,官方也不再维护了,所以新项目里尽量直接用 LMS,更可靠也更灵活。 这样解释,大家理解了吗?
同学们好,今天我们聊一聊在 Lightning Web Components 里处理事件的最佳实践。这个 Slide 把要点分成了两块,一块是自定义事件的“详细信息”,也就是 details;另一块是事件传播的配置。我们先看第一块。 想象一下,同一个组件内部的元素之间传信儿,就像你们在同一个教室说悄悄话,根本不需要写条子。这时候,你完全可以跳过 details 这个字段,直接用 event.target 拿到触发事件的元素,然后读它上面的属性。比如说 event.target.myProperty,对方想传什么值,直接挂在自己身上就行了,你从它身上一读就有。为什么这么简单?因为在同一个影子树内,event.target 还是那个真实的源头,没有变化。 那什么时候才需要用到 details 呢?是当你穿过了影子边界,也就是从一个组件穿越到另一个不相关的组件时。因为这时候浏览器为了保护封装,会把 event.target 重新指向到外层的元素,真实的发起者你就拿不到了。所以只能靠 details 把数据打包递过去。记住,只要跨了影子边界,就用 details。 好,如果你要用 details,有一条铁律:永远只传原始类型的数据,像字符串、数字、布尔这些。千万别把对象或者数组原样丢进去,因为传对象传的是引用,接收方万一改动了,你的源数据也跟着变了,这就是基于引用的变异,非常难排查。所以,如果你需要传复杂数据,先把它浅拷贝一份再放进去。尤其是那些带有 @api 或 @wire 反应式的属性,它们本身是只读代理对象,直接传可能出问题,复制一下最安全。 接下来看事件传播这一块。同学们,默认值是最好的朋友——bubble 设为 false,composed 也设为 false。这意味着你的事件只会在自己的组件里冒泡,不会穿过影子根到外面去。这样最好,因为保持了封装,外面的人不用管你内部怎么实现,你的事件就是自己用的,不是公共 API。 尽量别用组合事件,也就是 composed: true。为什么呢?因为它会让事件穿透多个影子边界,一路往上冒泡,等于你无意中向外界暴露了一大片契约。如果一定要用,千万记得给事件类型加上命名空间前缀,比如 myapp__myevent,保证全局唯一,避免和其他组件的事件名冲突。 最后,所有这一切的背后都有一个基本模式:数据向下流,事件向上走。父组件通过属性把数据传给孩子,孩子有变化需要通知父级,就用事件往上抛。这样单向的数据流既清晰又可控。 总结一下:内部通信直接读属性,不要 details;跨影子边界才用 details,并且只传原始值;事件默认不冒泡不合成为好,保持封装;实在需要合成事件,加命名空间;永远遵循“属性下传,事件上传”的原则。这些最佳实践,能让你的组件更健壮,少踩坑。 好了,今天就到这里,有疑问我们随时讨论。
好,同学们,我们这一页来看一个非常重要的细节,就是 `lwc:on` 这个指令在处理事件监听器时的变异规则。记住一句话就行:,永远不要直接去修改你传给 `lwc:on` 的那个对象,而是要用一个全新的对象来替换它。, 我知道你可能会想:“不就一个对象嘛,我改一下里面的属性,性能不是更好吗?” 但在 LWC 里,这样是不行的。因为框架在底层专门对 `lwc:on` 做了优化,它会去比较旧对象和新对象之间的差异。你想想看,如果你在原对象上直接增删属性,框架就分不清哪些监听器该注册、哪些该注销了。所以你每次想让事件配置发生变化时,都重新创建一个新对象赋值过去。 那框架具体是怎么处理的呢?它会对比新旧两个对象。旧对象里有、但新对象里没有的属性,框架就认为这个事件监听被删除了,它会帮你自动取消注册;新对象里多出来的属性,它就会当成新增的监听器注册上去;至于两个对象里都有的属性,它就认为这个监听器需要更新,可能是回调函数变了或者配置变了。这样一来,整个过程就是,可预测且安全,的。 我还想提醒一点,在 LWC 中,当你需要动态地往一个子组件上传递事件处理程序时,我们推荐你用 `lwc:on`,而不是用 `lwc:spread` 去拍散事件。`lwc:on` 是专门为这个场景设计的,用起来更清晰,性能也更好。 不过呢,有两个冲突的规则要特别注意一下。如果你在同一个 HTML 元素上,既用了 `lwc:on` 又用了 `on` 开头的属性来声明同样的事件类型,框架会直接报错,它不知道该听谁的。但如果你把这个 `on` 开头的属性换成 `lwc:spread`,反而不冲突了,结果那两个监听器都会被加上去。也就是说 `lwc:on` 和 `lwc:spread` 可以在相同事件类型上共存,但 `lwc:on` 和直接写的 `on...` 属性是不共存的。 最后,当你在创建动态组件的时候,经常会用到三个指令配合使用:`lwc:component` 加上 `lwc:is` 来指定要渲染哪个组件,`lwc:spread` 把所有属性拍进去,而 `lwc:on` 专门负责传入事件处理函数。这三个加在一起,你就拥有了在运行时灵活配置动态组件所有行为的能力。 所以这一页的重点就是,记住:,传给 `lwc:on` 的对象,永远更新时用新对象替换,而不是在原对象上动手脚,。这能帮你避免很多莫名其妙的 bug。好了,以上就是这节课关于 `lwc:on` 变异规则的内容。