如何指定 运行 嵌套 Web 组件构造函数的顺序?

How can I specify the sequence of running nested web components constructors?

我创建了两个网络组件并将其中一个嵌套到另一个中。 他们都有它的constructor。我遇到的问题是,我无法控制 运行 constructor 的顺序。 有什么办法可以解决这个问题吗?

这是我的代码:

子网络组件:

(function () {
    const template = document.createElement('template');
    template.innerHTML = `<div>WC1</div>`;

    class WC1Component extends HTMLElement {

        constructor() {
            super();
            console.log('WC1: constructor()');

            var me = this;

            me._shadowRoot = this.attachShadow({ 'mode': 'open' });
            me._shadowRoot.appendChild(template.content.cloneNode(true));
        }

        connectedCallback() {
            console.log('WC1: connectedCallback');
        }

        test() {
            console.log('test:wc1');
        }

    }

    window.customElements.define('wc-one', WC1Component);

}());

父网络组件:

(function () {
    const template = document.createElement('template');
    template.innerHTML = `
    <wc-one id="wc1"></wc-one>
    <div>WC2</div>
    `;

    class WC2Component extends HTMLElement {

        constructor() {
            super();
            console.log('WC2: constructor()');

            var me = this;

            me._shadowRoot = this.attachShadow({ 'mode': 'open' });
            me._shadowRoot.appendChild(template.content.cloneNode(true));
            me._wc1 = me._shadowRoot.querySelector('#wc1');
        }

        connectedCallback() {
            console.log('WC2: connectedCallback');
            this._wc1.test(); // <-- Error: test is undefined!
        }


    }

    window.customElements.define('wc-two', WC2Component);

}());

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Test Web Component</title>
</head>
<body>
    <script src="wc1.js"></script>
    <script src="wc2.js"></script>

    <wc-two></wc-two>
    <script>
    </script>

</body>
</html> 

结果:

WC2: constructor()
WC2: connectedCallback

Uncaught TypeError: this._wc1.test is not a function

WC1: constructor()
WC1: connectedCallback

您可以使用:

setTimeout(() => {
  this._wc1.test();
});

在 connectedCallback

实际上,您确实可以控制构造函数的 运行 序列。

由于 <wc-one> 是由 <wc-two> 创建的,因此 WC2 构造函数将始终在 WC1 之前被调用。

错误是因为当您尝试调用它时,内部组件 (WC1) 尚未添加到 DOM。

您可以监听 DOMContentLoaded 事件以确保元素正常,或者监听 window 上的 load 事件,或者实现 window.onload 处理程序。 @elanz-nasiri 也在工作。

DOMContentLoaded 将首先被解雇。

有替代解决方案,但更难实施。

window.customElements.define('wc-one', class extends HTMLElement {
    constructor() {
        super()
        this.attachShadow({ 'mode': 'open' }).innerHTML = `<div>WC1</div>`
    }
    test(source) {   
        console.log('test:wc1', source)
    }
} )

window.customElements.define('wc-two', class extends HTMLElement {
    constructor() {
        super()
        this._shadowRoot = this.attachShadow({ 'mode': 'open' })
        this._shadowRoot.innerHTML = `<wc-one id="wc1"></wc-one><div>WC2</div>`
        this._wc1 = this._shadowRoot.querySelector('#wc1');
    }
    connectedCallback() {
        setTimeout( ()=>this._wc1.test('setTimout') )            
        document.addEventListener( 'DOMContentLoaded', ()=>this._wc1.test('DOMContentLoaded') )
        window.onload = ()=>this._wc1.test('window.onload')
        window.addEventListener( 'load', ()=>this._wc1.test('load') )
    }
} )
<wc-two></wc-two>

如果您想控制何时调用构造函数,则需要调用它。不要让 HTML 解析器为你做这件事。允许 HTML 解析器执行此操作的问题在于您不知道 组件何时 升级。

const template = document.createElement('template');
template.innerHTML = `<div>WC1</div>`;

class WC1Component extends HTMLElement {
  constructor() {
    super();
    console.log('WC1: constructor()');

    this.attachShadow({ 'mode': 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }

  connectedCallback() {
    console.log('WC1: connectedCallback');
  }

  test() {
    console.log('test:wc1');
  }
}

window.customElements.define('wc-one', WC1Component);

//<wc-one id="wc1"></wc-one>
//<div>WC2</div>

class WC2Component extends HTMLElement {
  constructor() {
    super();
    console.log('WC2: constructor()');
    let wc1 = document.createElement('wc-one');
    wc1.id = 'wc1';

    this.attachShadow({ 'mode': 'open' });
    this._wc1 = wc1;
    this.shadowRoot.appendChild(wc1);
    let div = document.createElement('div');
    div.textContent = 'WC2';
    this.shadowRoot.appendChild(div);
  }

  connectedCallback() {
    console.log('WC2: connectedCallback');
    this._wc1.test();
  }
}

window.customElements.define('wc-two', WC2Component);
<wc-two></wc-two>

我所做的只是通过调用 document.createElement('wc-one') 使 wc-one 的构造函数执行,然后将其添加到组件中。这会强制该对象在我尝试使用其 test 函数之前存在。

此外,您不需要保存 this.attachShadow({ 'mode': 'open' }); 的 return 值,因为它已经作为 this.shadowRoot.

可用

您也不需要将 this 保存为 me