4

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

这是我的代码:

子网页组件:

(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);

}());

索引.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
4

3 回答 3

3

您可以使用:

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

在connectedCallback

于 2019-07-23T07:08:06.023 回答
1

如果要控制何时调用构造函数,则需要调用它。不要让 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>

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

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

您也不需要另存thisme.

于 2019-07-25T21:55:38.893 回答
0

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

因为<wc-one><wc-two>WC2 构造函数创建,所以总是在 WC1 之前调用。

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

您可以侦听DOMContentLoaded事件以确保元素正常,或load事件 on window,或实现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>

于 2019-07-23T16:37:45.833 回答