为什么我不能调用 WebComponent 的方法?
Why I can not call method of WebComponent?
我想知道如果我通过 .append
附加此组件而不是在模板中使用标签名称,为什么我不能调用在 web-component 中定义的方法。下面我提供几个例子。一个不工作(抛出错误)。我想知道为什么第一个示例会抛出此错误。
示例 1
const templateB = document.createElement('template');
templateB.innerHTML = `
<h1>ComponentB</h1>
`
class ComponentB extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.append(templateB.content.cloneNode(true));
}
hello() {
console.log('Hello');
}
}
customElements.define('component-b', ComponentB);
const templateA = document.createElement('template');
templateA.innerHTML = `
<div>
<component-b></component-b>
</div>
`;
class ComponentA extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.append(templateA.content.cloneNode(true));
this.componentB = this.shadowRoot.querySelector('component-b');
console.log(this.componentB instanceof ComponentB);
this.componentB.hello();
}
}
customElements.define('component-a', ComponentA);
document.body.append(new ComponentA());
在这个例子中,我在我的 js 文件中创建了一个网络组件,然后直接将它附加到文档中。在这种情况下,我得到一个错误,指出 .hello
在我的 ComponentB
中不存在。更重要的是,我使用 .querySelector
对 ComponentB
实例的引用不是 ComponentB
.
的实例
示例 2
const templateB = document.createElement('template');
templateB.innerHTML = `
<h1>ComponentB</h1>
`
class ComponentB extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.append(templateB.content.cloneNode(true));
}
hello() {
console.log('Hello');
}
}
customElements.define('component-b', ComponentB);
const templateA = document.createElement('template');
templateA.innerHTML = `
<div>
<component-b></component-b>
</div>
`;
class ComponentA extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.append(templateA.content.cloneNode(true));
this.componentB = this.shadowRoot.querySelector('component-b');
console.log(this.componentB instanceof ComponentB);
this.componentB.hello();
}
}
customElements.define('component-a', ComponentA);
<component-a></component-a>
在这个例子中,我将一个网络组件直接添加到 html 文件。在这种情况下,我没有收到错误,并且我使用 .querySelector
获得的对 ComponentB
实例的引用是 ComponentB
.
的实例
示例 3
const templateB = `
<h1>ComponentB</h1>
`;
class ComponentB extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.innerHTML = templateB;
}
hello() {
console.log('Hello');
}
}
customElements.define('component-b', ComponentB);
const templateA = `
<div>
<component-b></component-b>
</div>
`;
class ComponentA extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.innerHTML = templateA;
this.componentB = this.shadowRoot.querySelector('component-b');
console.log(this.componentB instanceof ComponentB);
this.componentB.hello();
}
}
customElements.define('component-a', ComponentA);
document.body.append(new ComponentA());
在这个例子中,我在我的 js 文件中创建了一个网络组件,然后直接将它附加到文档中。在这种情况下,我没有收到错误,并且我使用 .querySelector
获得的对 ComponentB
实例的引用是 ComponentB
的实例。 示例 1 和 示例 3 之间的唯一区别是我在这里使用 .innerHTML
而不是深度克隆模板。
从我的角度来看,示例 1 是正确的,应该可以工作。谁能向我解释为什么我错了以及为什么它不起作用?也许你也可以提供一个解决方案,我如何在 js 文件中使用 <template>
+ .cloneNode
来访问我以这种方式创建的网络组件的方法?
https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/upgrade
我首先注意到,如果我在尝试调用 componentB 方法之前添加一个小的 setTimeout,那么示例 1 工作正常。通过进一步的调整,我能够让示例 1 大部分按原样工作,唯一的变化是在尝试访问其任何方法之前,我在组件 B 引用上调用了上面文档 link 中描述的方法。
customElements.upgrade(this.componentB);
我会尝试解释,但老实说,Web 组件生命周期挂钩的细微差别和确切时间让我仍然有些困惑。当第一次尝试访问其方法时,这显然是组件未在自定义元素注册表中完全注册的问题,我只是不确定为什么其他 2 个示例按预期工作,而第一个示例需要显式'upgrading'.
简单说明:
.innerHTML
是 同步
因此<div><component-b></component-b></div>
在构造Component-A时立即解析
.append
with Templates 是 A-synchronous,它将创建Component A shadowDOM 中的 HTML,但将解析留给稍后
我清理了您的代码以仅显示相关部分,并添加了 console.log
以显示 组件 B 何时构建
您可以玩组件 A 中的 append/append/innerHTML
行
(复杂解释)深度视频:https://www.youtube.com/watch?v=8aGhZQkoFbQ
注意:您实际上应该尝试避免 this.componentB.hello
风格编码,
因为它在组件 之间创建了紧密耦合。即使 B 尚不存在,组件 A 也应该可以工作。是的,这需要更复杂的编码(事件、承诺等)。如果您有紧密耦合的组件,您应该考虑将它们设为 1 个组件。
<script>
customElements.define('component-b', class extends HTMLElement {
constructor() {
console.log("constructor B");
super().attachShadow({mode: "open"}).innerHTML = "<h1>ComponentB</h1>";
}
hello() {
console.log('Hello');
}
});
const templateA = document.createElement('template');
templateA.innerHTML = `<div><component-b></component-b></div>`;
customElements.define('component-a', class extends HTMLElement {
constructor() {
console.log("constructor A");
super().attachShadow({mode: "open"})
.append(templateA.content.cloneNode(true));
//.append(document.createElement("component-b"));
//.innerHTML = "<div><component-b></component-b></div>";
this.componentB = this.shadowRoot.querySelector('component-b');
console.assert(this.componentB.hello,"component B not defined yet");
}
});
document.body.append(document.createElement("component-a"));
</script>
我想知道如果我通过 .append
附加此组件而不是在模板中使用标签名称,为什么我不能调用在 web-component 中定义的方法。下面我提供几个例子。一个不工作(抛出错误)。我想知道为什么第一个示例会抛出此错误。
示例 1
const templateB = document.createElement('template');
templateB.innerHTML = `
<h1>ComponentB</h1>
`
class ComponentB extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.append(templateB.content.cloneNode(true));
}
hello() {
console.log('Hello');
}
}
customElements.define('component-b', ComponentB);
const templateA = document.createElement('template');
templateA.innerHTML = `
<div>
<component-b></component-b>
</div>
`;
class ComponentA extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.append(templateA.content.cloneNode(true));
this.componentB = this.shadowRoot.querySelector('component-b');
console.log(this.componentB instanceof ComponentB);
this.componentB.hello();
}
}
customElements.define('component-a', ComponentA);
document.body.append(new ComponentA());
在这个例子中,我在我的 js 文件中创建了一个网络组件,然后直接将它附加到文档中。在这种情况下,我得到一个错误,指出 .hello
在我的 ComponentB
中不存在。更重要的是,我使用 .querySelector
对 ComponentB
实例的引用不是 ComponentB
.
示例 2
const templateB = document.createElement('template');
templateB.innerHTML = `
<h1>ComponentB</h1>
`
class ComponentB extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.append(templateB.content.cloneNode(true));
}
hello() {
console.log('Hello');
}
}
customElements.define('component-b', ComponentB);
const templateA = document.createElement('template');
templateA.innerHTML = `
<div>
<component-b></component-b>
</div>
`;
class ComponentA extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.append(templateA.content.cloneNode(true));
this.componentB = this.shadowRoot.querySelector('component-b');
console.log(this.componentB instanceof ComponentB);
this.componentB.hello();
}
}
customElements.define('component-a', ComponentA);
<component-a></component-a>
在这个例子中,我将一个网络组件直接添加到 html 文件。在这种情况下,我没有收到错误,并且我使用 .querySelector
获得的对 ComponentB
实例的引用是 ComponentB
.
示例 3
const templateB = `
<h1>ComponentB</h1>
`;
class ComponentB extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.innerHTML = templateB;
}
hello() {
console.log('Hello');
}
}
customElements.define('component-b', ComponentB);
const templateA = `
<div>
<component-b></component-b>
</div>
`;
class ComponentA extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.shadowRoot.innerHTML = templateA;
this.componentB = this.shadowRoot.querySelector('component-b');
console.log(this.componentB instanceof ComponentB);
this.componentB.hello();
}
}
customElements.define('component-a', ComponentA);
document.body.append(new ComponentA());
在这个例子中,我在我的 js 文件中创建了一个网络组件,然后直接将它附加到文档中。在这种情况下,我没有收到错误,并且我使用 .querySelector
获得的对 ComponentB
实例的引用是 ComponentB
的实例。 示例 1 和 示例 3 之间的唯一区别是我在这里使用 .innerHTML
而不是深度克隆模板。
从我的角度来看,示例 1 是正确的,应该可以工作。谁能向我解释为什么我错了以及为什么它不起作用?也许你也可以提供一个解决方案,我如何在 js 文件中使用 <template>
+ .cloneNode
来访问我以这种方式创建的网络组件的方法?
https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/upgrade
我首先注意到,如果我在尝试调用 componentB 方法之前添加一个小的 setTimeout,那么示例 1 工作正常。通过进一步的调整,我能够让示例 1 大部分按原样工作,唯一的变化是在尝试访问其任何方法之前,我在组件 B 引用上调用了上面文档 link 中描述的方法。
customElements.upgrade(this.componentB);
我会尝试解释,但老实说,Web 组件生命周期挂钩的细微差别和确切时间让我仍然有些困惑。当第一次尝试访问其方法时,这显然是组件未在自定义元素注册表中完全注册的问题,我只是不确定为什么其他 2 个示例按预期工作,而第一个示例需要显式'upgrading'.
简单说明:
.innerHTML
是 同步
因此<div><component-b></component-b></div>
在构造Component-A时立即解析
.append
with Templates 是 A-synchronous,它将创建Component A shadowDOM 中的 HTML,但将解析留给稍后
我清理了您的代码以仅显示相关部分,并添加了 console.log
以显示 组件 B 何时构建
您可以玩组件 A 中的 append/append/innerHTML
行
(复杂解释)深度视频:https://www.youtube.com/watch?v=8aGhZQkoFbQ
注意:您实际上应该尝试避免 this.componentB.hello
风格编码,
因为它在组件 之间创建了紧密耦合。即使 B 尚不存在,组件 A 也应该可以工作。是的,这需要更复杂的编码(事件、承诺等)。如果您有紧密耦合的组件,您应该考虑将它们设为 1 个组件。
<script>
customElements.define('component-b', class extends HTMLElement {
constructor() {
console.log("constructor B");
super().attachShadow({mode: "open"}).innerHTML = "<h1>ComponentB</h1>";
}
hello() {
console.log('Hello');
}
});
const templateA = document.createElement('template');
templateA.innerHTML = `<div><component-b></component-b></div>`;
customElements.define('component-a', class extends HTMLElement {
constructor() {
console.log("constructor A");
super().attachShadow({mode: "open"})
.append(templateA.content.cloneNode(true));
//.append(document.createElement("component-b"));
//.innerHTML = "<div><component-b></component-b></div>";
this.componentB = this.shadowRoot.querySelector('component-b');
console.assert(this.componentB.hello,"component B not defined yet");
}
});
document.body.append(document.createElement("component-a"));
</script>