按 "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.

的常规脚本相同的执行队列

Source

当您无法控制元素的加载顺序时,
你必须在你的元素

中处理依赖关系

使用: 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

.

成功遇见了最伟大的人