Web 组件:如何从父组件访问子组件实例

Web Components: how to access child component instances from a parent

编辑 我在这个问题中遗漏了一些细节,但假设这两个元素都已经定义。此外,在现实世界的问题中,DOM 树更大,子组件 插入 到父组件中。找到子组件没有问题。

我有两个网络组件:ParentChild

我可能会像这样在标记中使用这些组件:

<parent-element>
  <child-element></child-element>
</parent-element>

Child 组件具有我想向父级公开的逻辑。

class Child extends HTMLElement {
  constructor() {
    ...
  }

  publicMethod() {
    console.log('call me from the parent component');
  }
}

理想情况下,我想像这样从父级调用子方法:

class Parent extends HTMLElement {
  constructor() {
    ...
  }

  someTrigger() {
    const child = this.shadowRoot.querySelector('child-element');
    child.publicMethod();
  }
}

但是这不起作用,因为 child 是一个 HTMLElement,因为 querySelector() API returns 一个 HTMLElement而不是 Web 组件 instance.

有没有办法以类似的方式获取组件的实例?我希望能够遍历 Parent 组件影子树以找到特定的组件实例。

小心 whenDefined;
它告诉您 Web 组件是 定义的 ,而不是当它在 DOM:

中被 解析

只要保持<child-element>lightDOM<slot>和shadowDOM没有关系和他们在一起。

.as-console-wrapper {max-height:100%!important;top:0;zoom:.88}
.as-console-row:nth-child(n-6) {background:#0057B7!important;color:#FFD500!important}
.as-console-row:nth-child(n+6) {background:#FFD500!important;color:#0057B7!important}
.as-console-row-code{padding:0!important}
<script>
  console.clear();
  class Component extends HTMLElement {
    constructor() {
      super();
      console.log(this.nodeName, "constructor")
    }
    connectedCallback(){
      console.log("connectedCallback", this.nodeName, this.children.length, "children");      
    }
  }
  customElements.define("child-component", class extends Component {
    foo() {
      console.log("Executed <child-element>.foo()");
    }
  });
  customElements.define("parent-component", class extends Component {
    connectedCallback() {
      super.connectedCallback();
      customElements.whenDefined('child-component')
                    .then(() => console.log("Promise resolved, <child-component> IS defined!"));
      setTimeout(() => { // wait till lightDOM is parsed
        console.log("After setTimeout",this.nodeName,"has", this.children.length, "children");
        this.querySelectorAll("child-component").forEach(child => child.foo());
      })
    }
  })
</script>
<parent-component>
  <child-component></child-component>
  <child-component></child-component>
</parent-component>