如何在 Web Components 中捕获自定义事件?

How to catch custom events in Web Components?

为了让 Web 组件相互通信,我使用了自定义事件。让我们想象一下:

WebComponentA 使用或包含 WebComponentB,它在单击按钮时发送 CustomEvent(气泡:真,组合:真)。如果 WebComponentB 发送此事件,WebComponentA 想要执行某些操作。

我应该如何在 WebComponentB 中调度事件?

window.dispatchEvent(customEvent);

this.shadowRoot.dispatchEvent(customEvent);

我应该如何捕捉 WebComponentA 中的事件?

window.addEventListener(custom-event, () => {
);

this.shadowRoot.addEventListener(custom-event, () => {
);

我应该考虑使用一个或另一个有什么负面影响吗?

谢谢!

使用 bubble: true, composed: true 在组件本身上调度事件,这样事件就会冒泡到任何监视它的东西。当您非常具体地希望有很多可预测性和共享状态时,我的意思是这确实需要紧密耦合,那么只需在全局 self(即浏览器中的 window)上编排事件。这里有一些我希望有所帮助的随机例子,他们所做的只是为了展示一个例子而相对无用。总体想法是在有意义的情况下松散地耦合事物,事件只是传递与状态变化相关的消息。组件可以始终是完全隔离的,无论它在什么上下文中运行,都可以单独关注它对该信息的处理(准备和接收模型——这是功能模式高度适用的地方)。如果需要更多细节或令人困惑,请随意拼写出来,我们可以尽力提供帮助。

另请注意,因为我没有设置任何 shadowRoot,所以组合标志在此示例中根本没有用。

广泛地说:

  • 全局:selfwindow 的同义词或其他上下文中的工人);在这里协调旨在紧密耦合许多事物的事件——当需要非常具体的协调系统时,它是迄今为止最简单的组织方案;对于这种情况,只需在此处收听和分发事件即可;在 connectedCallbackdisconnectedCallback 中添加和删除事件侦听器。然后要么用冒泡发送到任何地方,要么直接 self.dispatchEvent('type', {detail:...}) 并且不再需要冒泡等。

  • 节点:在树的任何级别,当组件具有任何状态或任何类型的事件时,处理场景并创建适当的消息作为 event.detail 和一个合理的名称作为 event.type,然后从处理该逻辑的节点分派它。其他节点——在其自身上作为父节点等——可以监视 event.bubble 向上,并且当从影子节点调度时,这些事件可以使用 composed:true 标志以允许事件在 shadowRoot 之外继续.或者该元素可以处理内部事件并分派适合该类型的新型事件和负载。

      <my-global>my global </my-global>
      <my-aye> aye (parent)
          <my-bee> bee (child) </my-bee>
      </my-aye>
      <my-bee> bee </my-bee>
    
    
      function eventdetails(event){
          const {type, detail} = event;
          console.log({type, detail, self:this, path: event.composedPath(), event});
      }
    
      customElements.define('my-global', class MyGlobalWatch extends HTMLElement{
          constructor(){
              super();
              this.global = this.global.bind(this);
          }
          global(event){
              eventdetails.call(this, event);
          }
          connectedCallback(){
              self.addEventListener('global', this.global);
          }
          disconnectedCallback(){
              self.removeEventListener('global', this.global);
          }
      });
      customElements.define('my-aye', class MyAye extends HTMLElement{
          constructor(){
              super();
              this.addEventListener('hi-aye', this.handle);
              this.addEventListener('hi-bee', this.handle);
          }
          handle(event){
              eventdetails.call(this, event);
              if(event.type === 'hi-bee'){
                  self.dispatchEvent(new CustomEvent('global', {detail: event.detail, cancelable: true, composed: true, bubbles: false}));
              }
          }
      });
      customElements.define('my-bee', class MyBee extends HTMLElement{
          constructor(){
              super();
              this.addEventListener('hi-aye', this.handle);
              this.addEventListener('hi-bee', this.handle);
              this.ticker = this.ticker.bind(this);
          }
          handle(event){
              eventdetails.call(this, event);
          }
          ticker(){
              // 3 events of the same type, different configuration
              this.dispatchEvent(new CustomEvent('hi-aye', {detail: {payload:'> -composed +bubbles'}, cancelable: true, composed: false, bubbles: true}));
              this.dispatchEvent(new CustomEvent('hi-aye', {detail: {payload:'> +composed +bubbles'}, cancelable: true, composed: true, bubbles: true}));
              this.dispatchEvent(new CustomEvent('hi-aye', {detail: {payload:'> -composed -bubbles'}, cancelable: true, composed: false, bubbles: false}));
    
              this.dispatchEvent(new CustomEvent('hi-bee', {detail: {stuff:'things'}, cancelable: true, composed: true, bubbles: true}));
    
              this._timer = setTimeout(this.ticker, 1234);
          }
          connectedCallback(){
              this.ticker();
          }
          disconnectedCallback(){
              clearTimeout(this._timer);
          }
      });