Web组件(本机UI)之间如何通信?

San*_*eep 4 html javascript web-component custom-element native-web-component

我正在尝试为我的UI项目之一使用本机Web组件,并且对于此项目,我没有使用任何框架或库(例如Polymer等)。我想知道在两者之间有什么最佳的交流方式或其他方式像我们在angularjs / angular中所做的那样的Web组件(例如消息总线概念)。

当前,在UI Web组件中,我正在使用dispatchevent发布数据和接收数据,正在使用addeventlistener。例如,有2个Web组件,ChatForm和ChatHistory。

// chatform webcomponent on submit text, publish chattext data 
this.dispatchEvent(new CustomEvent('chatText', {detail: chattext}));

// chathistory webcomponent, receive chattext data and append it to chat list
this.chatFormEle.addEventListener('chatText', (v) => {console.log(v.detail);});
Run Code Online (Sandbox Code Playgroud)

请让我知道实现此目的的其他方法。任何可以轻松与本机UI Web组件集成的优秀库,如postaljs等。

Dan*_*man 6

对于其他两个答案+1,事件是最好的,因为组件是松散耦合的


另请参阅:https://pm.dartus.fr/blog/a-complete-guide-on-shadow-dom-and-event-propagation/


请注意,在detail自定义事件中,您可以发送任何您想要的内容。

事件驱动函数执行:

所以我使用(伪代码):

定义纸牌/空当接龙游戏的元素:

-> game Element
  -> pile Element
    -> slot Element
      -> card element
  -> pile Element
    -> slot Element
      -> empty
Run Code Online (Sandbox Code Playgroud)

当一张牌(由用户拖动)需要移动到另一堆时,

它发送一个事件(将 DOM 冒泡到游戏元素)

    //triggered by .dragend Event
    card.say(___FINDSLOT___, {
                                id, 
                                reply: slot => card.move(slot)
                            });    
Run Code Online (Sandbox Code Playgroud)

注: reply是函数定义

因为所有的桩都被告知要监听___FINDSLOT___游戏元素中的事件......

   pile.on(game, ___FINDSLOT___, evt => {
                                      let foundslot = pile.free(evt.detail.id);
                                      if (foundslot.length) evt.detail.reply(foundslot[0]);
                                    });

Run Code Online (Sandbox Code Playgroud)

只有一堆匹配的evt.detail.id响应:

!!!通过执行card发送的函数evt.detail.reply

并获取技术信息:该函数在pile范围内执行!

以上代码为伪代码!

为什么?!

可能看起来很复杂;
重要的部分是pile元素没有耦合到元素.move()中的方法card

唯一耦合是事件的名称:___FINDSLOT___ !!!

这意味着card始终处于控制之中,并且相同的事件(名称)可用于:

  • 一张卡可以去哪里?
  • 最好的位置在哪里?
  • 河牌 中哪张牌pile构成葫芦?
  • ...

在我的 E-lements 代码中,两者都pile没有耦合evt.detail.id

CustomEvents仅发送函数



.say()和是.on()我的自定义方法(在每个元素上)dispatchEventaddEventListener

我现在有一些电子元素可以用来创建任何纸牌游戏

不需要任何库,编写您自己的“消息总线”

我的element.on()方法只是在addEventListener函数周围包裹了几行代码,因此可以轻松删除它们:

    $Element_addEventListener(
        name,
        func,
        options = {}
    ) {
        let BigBrotherFunc = evt => {                     // wrap every Listener function
            if (evt.detail && evt.detail.reply) {
                el.warn(`can catch ALL replies '${evt.type}' here`, evt);
            }
            func(evt);
        }
        el.addEventListener(name, BigBrotherFunc, options);
        return [name, () => el.removeEventListener(name, BigBrotherFunc)];
    },
    on(
        //!! no parameter defintions, because function uses ...arguments
    ) {
        let args = [...arguments];                                  // get arguments array
        let target = el;                                            // default target is current element
        if (args[0] instanceof HTMLElement) target = args.shift();  // if first element is another element, take it out the args array
        args[0] = ___eventName(args[0]) || args[0];                 // proces eventNR
        $Element_ListenersArray.push(target.$Element_addEventListener(...args));
    },
Run Code Online (Sandbox Code Playgroud)

.say( )是一个单线:

    say(
        eventNR,
        detail,             //todo some default something here ??
        options = {
            detail,
            bubbles: 1,    // event bubbles UP the DOM
            composed: 1,   // !!! required so Event bubbles through the shadowDOM boundaries
        }
    ) {
        el.dispatchEvent(new CustomEvent(___eventName(eventNR) || eventNR, options));
    },
Run Code Online (Sandbox Code Playgroud)


Kam*_*ski 6

工作示例

在您的父代码 (html/css) 中,您应该订阅由执行其方法<chat-form>发出的事件并发送事件数据<chat-history>add在下面的工作示例中)

// WEB COMPONENT 1: chat-form
customElements.define('chat-form', class extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `Form<br><input id="msg" value="abc"/>
      <button id="btn">send</button>`;

    btn.onclick = () => {
      // can: this.onsend() or not recommended: eval(this.getAttribute('onsend'))
      this.dispatchEvent(new CustomEvent('send',{detail: {message: msg.value} }))
      msg.value = '';
    }
  }
})


// WEB COMPONENT 2: chat-history
customElements.define('chat-history', class extends HTMLElement {
  add(msg) {
    let s = ""
    this.messages = [...(this.messages || []), msg];
    for (let m of this.messages) s += `<li>${m}</li>`
    this.innerHTML = `<div><br>History<ul>${s}</ul></div>`
  }
})


// -----------------
// PARENT CODE
// which subscribe chat-form send event, 
// receive message and set it to chat-history
// -----------------

myChatForm.addEventListener('send', e => {
  myChatHistory.add(e.detail.message)
});
Run Code Online (Sandbox Code Playgroud)
body {background: white}
Run Code Online (Sandbox Code Playgroud)
<h3>Hello!</h3>

<chat-form id="myChatForm"></chat-form>

<div>Type something</div>

<chat-history id="myChatHistory"></chat-history>
Run Code Online (Sandbox Code Playgroud)


Int*_*lia 5

如果您将Web组件视为内置组件之类<div><audio>则可以回答自己的问题。这些组件不会互相通信。

一旦开始允许组件彼此直接通信,那么您实际上就没有真正拥有绑定在一起的系统的组件,没有组件B就无法使用组件A。这太紧密地绑在一起了。

而是在拥有两个组件的父代码中添加代码,以使您能够接收来自组件A的事件调用函数或在组件B中设置参数,反之亦然。

话虽如此,内置组件对此规则有两个例外:

  1. <label>标签:它使用for属性采取另一个组件的ID,如果设置和有效的,那么它通过专注于其他组件时,你点击<label>

  2. <form>标签:这看起来对于那些孩子们聚集在一起,发布形式所需要的数据表单元素。

但是,这两者仍然没有束缚。在<label>被告知的收件人focus时,只将它传递如果ID设置和有效或第一成形件作为一个孩子。而且,该<form>元素并不关心存在哪些子元素,或者仅通过其所有后代而找到多少子元素,这些子元素即为表单元素并获取其value属性。

但是通常,您应该避免让一个同级组件直接与另一同级对话。上面两个示例中的交叉通信方法可能是唯一的例外。

相反,您的父代码应侦听事件并调用函数或设置属性。

是的,您可以将该功能包装在一个新的父组件中,但是请您节省很多时间并避免使用意大利面条式的代码。

通常,我绝不允许兄弟姐妹之间互相交谈,而他们与父母交谈的唯一方法是通过事件。父母可以通过属性,属性和功能直接与子女交谈。但是在所有其他情况下都应避免使用它。