按 "wrong" 顺序导入 Web 组件时出错
Error when importing web components in the "wrong" order
我已经构建了一个包含几个 HTML Web 组件的小型库,供公司内部使用。有些组件相互依赖,所以我也相互导入它们。直到最近,我对这种方法没有任何严重的问题,但现在我在加载使用此类相互依赖的组件的 HTML 页面时遇到错误消息。
我已经在一个小例子中隔离了这个问题。请查看以下三个文件。
test-container.js
import { TestItem } from "./test-item";
export class TestContainer extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" }).innerHTML = `
<style>
* {
position: relative;
box-sizing: border-box;
}
:host {
contain: content;
display: block;
}
</style>
<div>
<slot></slot>
</div>
`;
}
connectedCallback() {
if (!this.isConnected) {
return;
}
for (const node of this.childNodes) {
if (node instanceof TestItem) {
//...
}
}
}
}
customElements.define("test-container", TestContainer);
test-item.js
import { TestContainer } from "./test-container";
export class TestItem extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" }).innerHTML = `
<style>
* {
position: relative;
box-sizing: border-box;
}
:host {
contain: content;
display: block;
}
</style>
<div>
<slot></slot>
</div>
`;
}
}
customElements.define("test-item", TestItem);
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test</title>
<script type="module" src="/test-container"></script>
<script type="module" src="/test-item"></script>
<style>
test-container {
width: 600px;
height: 400px;
background: lightblue;
border: 1px solid;
}
test-item {
width: 200px;
height: 200px;
background: lightgreen;
border: 1px solid;
}
</style>
</head>
<body>
<test-container>
<test-item></test-item>
</test-container>
</body>
</html>
这段代码似乎工作正常。
但是,如果我在 index.html
文件中切换两个 <script>
标签,开发人员工具控制台会显示以下错误:
Uncaught ReferenceError: Cannot access 'TestItem' before initialization
at HTMLElement.connectedCallback (test-container:30)
at test-container:37
由于我在许多组件中导入了多个模块,因此我想按字母顺序对它们进行排序(为了清楚起见)。在我的测试示例中它很好,但在我的实际代码中它不是...
所以基本上我希望我的模块完全独立于它们被其他模块导入的顺序。有什么办法可以实现吗?
非常欢迎所有建议。但是,我不允许安装和使用任何 external/3rd 派对包。甚至不允许使用 jQuery。所以一个解决方案应该只包含普通的 vanilla JS、普通的 CSS 和普通的 HTML5,并且它至少应该在最新的 Google Chrome 和 Mozilla Firefox 中正常工作网络浏览器。
可能会有帮助
<!-- This script will execute after… -->
<script type="module" src="1.mjs"></script>
<!-- …this script… -->
<script src="2.js"></script>
<!-- …but before this script. -->
<script defer src="3.js"></script>
顺序应该是2.js, 1.mjs, 3.js.
脚本在获取期间阻止 HTML 解析器的方式很糟糕。
对于常规脚本,您可以使用 defer
来防止阻塞,这也会延迟脚本执行直到文档完成解析,并与其他延迟脚本保持执行顺序。
默认情况下,模块脚本的行为类似于 defer
– 无法使模块脚本在获取时阻止 HTML 解析器。
模块脚本使用与使用 defer
.
的常规脚本相同的执行队列
当您无法控制元素的加载顺序时,
你必须在你的元素
中处理依赖关系
使用: https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/whenDefined
whenDefined returns 承诺!
因此您的 <test-container>
代码需要如下内容:
customElements.whenDefined('test-item')
.then( () => {
//execute when already exist or became available
});
https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/whenDefined
有一个更详细的示例等待页面中所有未定义的元素
依赖关系
事件驱动方法可能更好地摆脱依赖性。
使 <test-item>
在 connectedCallback
中调度事件 X
<test-container>
监听事件 X 并对项目
做一些事情
然后您可以将 <another-item>
添加到组合中 而无需 更改 <test-container>
也许默认的 slotchange
事件可以提供帮助:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLSlotElement/slotchange_event
.
成功遇见了最伟大的人
我已经构建了一个包含几个 HTML Web 组件的小型库,供公司内部使用。有些组件相互依赖,所以我也相互导入它们。直到最近,我对这种方法没有任何严重的问题,但现在我在加载使用此类相互依赖的组件的 HTML 页面时遇到错误消息。
我已经在一个小例子中隔离了这个问题。请查看以下三个文件。
test-container.js
import { TestItem } from "./test-item";
export class TestContainer extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" }).innerHTML = `
<style>
* {
position: relative;
box-sizing: border-box;
}
:host {
contain: content;
display: block;
}
</style>
<div>
<slot></slot>
</div>
`;
}
connectedCallback() {
if (!this.isConnected) {
return;
}
for (const node of this.childNodes) {
if (node instanceof TestItem) {
//...
}
}
}
}
customElements.define("test-container", TestContainer);
test-item.js
import { TestContainer } from "./test-container";
export class TestItem extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" }).innerHTML = `
<style>
* {
position: relative;
box-sizing: border-box;
}
:host {
contain: content;
display: block;
}
</style>
<div>
<slot></slot>
</div>
`;
}
}
customElements.define("test-item", TestItem);
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test</title>
<script type="module" src="/test-container"></script>
<script type="module" src="/test-item"></script>
<style>
test-container {
width: 600px;
height: 400px;
background: lightblue;
border: 1px solid;
}
test-item {
width: 200px;
height: 200px;
background: lightgreen;
border: 1px solid;
}
</style>
</head>
<body>
<test-container>
<test-item></test-item>
</test-container>
</body>
</html>
这段代码似乎工作正常。
但是,如果我在 index.html
文件中切换两个 <script>
标签,开发人员工具控制台会显示以下错误:
Uncaught ReferenceError: Cannot access 'TestItem' before initialization
at HTMLElement.connectedCallback (test-container:30)
at test-container:37
由于我在许多组件中导入了多个模块,因此我想按字母顺序对它们进行排序(为了清楚起见)。在我的测试示例中它很好,但在我的实际代码中它不是...
所以基本上我希望我的模块完全独立于它们被其他模块导入的顺序。有什么办法可以实现吗?
非常欢迎所有建议。但是,我不允许安装和使用任何 external/3rd 派对包。甚至不允许使用 jQuery。所以一个解决方案应该只包含普通的 vanilla JS、普通的 CSS 和普通的 HTML5,并且它至少应该在最新的 Google Chrome 和 Mozilla Firefox 中正常工作网络浏览器。
可能会有帮助
<!-- This script will execute after… -->
<script type="module" src="1.mjs"></script>
<!-- …this script… -->
<script src="2.js"></script>
<!-- …but before this script. -->
<script defer src="3.js"></script>
顺序应该是2.js, 1.mjs, 3.js.
脚本在获取期间阻止 HTML 解析器的方式很糟糕。
对于常规脚本,您可以使用 defer
来防止阻塞,这也会延迟脚本执行直到文档完成解析,并与其他延迟脚本保持执行顺序。
默认情况下,模块脚本的行为类似于 defer
– 无法使模块脚本在获取时阻止 HTML 解析器。
模块脚本使用与使用 defer
.
当您无法控制元素的加载顺序时,
你必须在你的元素
使用: https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/whenDefined
whenDefined returns 承诺!
因此您的 <test-container>
代码需要如下内容:
customElements.whenDefined('test-item')
.then( () => {
//execute when already exist or became available
});
https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/whenDefined
有一个更详细的示例等待页面中所有未定义的元素
依赖关系
事件驱动方法可能更好地摆脱依赖性。
使 <test-item>
在 connectedCallback
<test-container>
监听事件 X 并对项目
然后您可以将 <another-item>
添加到组合中 而无需 更改 <test-container>
也许默认的 slotchange
事件可以提供帮助:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLSlotElement/slotchange_event
.
成功遇见了最伟大的人