【发布时间】:2021-11-18 10:55:11
【问题描述】:
我构建了一个 Web 组件,并在其构造函数中使用模板来初始化其子结构。这个子结构的一部分是另一个我希望调用它的 setter 方法的 web 组件。我通过查询通过模板创建的 DOM 树来获取子组件,但是通过这个只有标准的Element 属性可以访问。
这是一个有点复杂的问题,可能是我遗漏了一些基本的东西。这似乎与一个 Web 组件通过模板克隆使用另一个 Web 组件这一事实有关。有人建议 in this question 问题可能是由于未加载/定义子组件。我不明白这一点,特别是因为我无法让建议的解决方案发挥作用。我还假设正在运行的任何 JS 引擎浏览器都足够智能以解决 import 依赖项,并且如果其导入尚未准备好,则不会运行代码。我是不是过于简单化了?
确定性失败的简单可重现示例是必不可少的。所以我设法创建了一个简单的副本来演示这个问题。为了保持一致性,我使用了与原始设计相同的多文件结构:
- component_a.js
class CompA extends HTMLElement
{
constructor()
{
super();
this.attachShadow({mode: "open"});
this.shadowRoot.append(CompA.template.content.cloneNode(true));
this._value = 0;
this.shadowRoot.getElementById('top').innerHTML = "A=" + this._value;
}
set value(x)
{
this._value = 2*x;
this.shadowRoot.getElementById('top').innerHTML = "A=" + this._value;
console.log('Value set on CompA');
}
}
CompA.template = document.createElement("template");
CompA.template.innerHTML = `<div id='top'></div>`;
customElements.define("comp-a", CompA);
export { CompA };
- component_b.js
import { CompA } from "./component_a.js"
class CompB extends HTMLElement
{
constructor()
{
super();
this.attachShadow({mode: "open"});
this.shadowRoot.append(CompB.template.content.cloneNode(true));
let s = this.shadowRoot.getElementById('subcomponent');
console.log(s.constructor.name);
console.log(s.matches(':defined'));
s.value = 1;
}
set value(x)
{
this.shadowRoot.getElementById('subcomponent').value = x;
console.log('Value set on CompB');
}
}
CompB.template = document.createElement("template");
CompB.template.innerHTML = `<div>
<span>Component B:</span>
<comp-a id='subcomponent'></comp-a>
</div>`;
customElements.define("comp-b", CompB);
export { CompB };
- question.js
import { CompB } from "./component_b.js"
window.onload = (event) =>
{
let x = document.createElement('comp-b');
document.body.append(x);
x.value = 10;
}
- question.html
<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<title>question</title>
<script type='module' src='./question.js'></script>
</head>
<body>
</body>
</html>
预期行为:
在 question.html 上,负载 <comp-b> 被创建并插入到页面中。它的 setter 方法被调用,参数为 10。在其构造函数 <comp-a> 中创建 <comp-b> 期间,通过提供的模板附加。一旦实例化,它就会被变量s 引用,并且应该调用它的setter 方法——产生A=20innerHTML。所以页面应该是:
Component B:
A=20
使用预期的控制台输出:
CompB
true
Value set on CompA
Value set on CompA
Value set on CompB
观察到的行为:
变量s 确实指向了正确的元素,但是s.value = 1; 并没有调用CompA 的setter,而是简单地将值为1 的属性分配给元素,从而保持其默认值。该页面是:
Component B:
A=0
使用控制台输出:
HTMLElement
false
Value set on CompB
问题:
有人可以解释一下为什么这会失败以及如何强制 JS 将整个指定的CompA 与s 相关联,而不仅仅是Element?
请随时提出进一步可能的问题诊断建议?
【问题讨论】:
-
嗨!请将代码缩减为 minimal reproducible example 来演示问题,最好是使用 Stack Snippets(
[<>]工具栏按钮)的 runnable 代码; here's how to do one. -
@T.J.在这里很难模拟导入,不是吗?
-
@connexo - 是的,很遗憾,但这无关紧要。问题不在于导入/导出。在这种情况下,minimal reproducible example sn-p 是完全可行的。
-
@T.J.Crowder kind of 是关于导入的,因为假设
CompA导入CompB应该运行对customElements.define('comp-b', CompB)的调用,所以基本上OP 不应该遇到任何问题。