一种延迟加载自定义元素的方法
创始人
2024-06-01 13:48:36

您可能实际上并不需要所有这些;通常有一个更简单的方法。如果有意使用,此处显示的技术可能仍然对您的工具集有用。

为了保持一致性,我们希望我们的自动加载器也成为一个自定义元素——这也意味着我们可以通过 HTML 轻松配置它。但首先,让我们逐步确定那些未解决的自定义元素:

class AutoLoader extends HTMLElement {connectedCallback() {let scope = this.parentNode;this.discover(scope);}
}
customElements.define("ce-autoloader", AutoLoader);

假设我们已经预先加载了这个模块(使用async是理想的),我们可以将一个元素放入我们的文档中。这将立即启动 的所有子元素的发现过程,这些子元素现在构成了我们的根元素。我们可以通过添加到相应的容器元素来将发现限制在文档的子树中——实际上,我们甚至可以为不同的子树设置多个实例。

当然,我们仍然必须实现该discover方法(作为上面类的一部分AutoLoader):

discover(scope) {let candidates = [scope, ...scope.querySelectorAll("*")];for(let el of candidates) {let tag = el.localName;if(tag.includes("-") && !customElements.get(tag)) {this.load(tag);}}
}

在这里,我们检查我们的根元素以及每个后代 ( *)。如果它是一个自定义元素——如带连字符的标签所示——但尚未升级,我们将尝试加载相应的定义。以这种方式查询 DOM 可能代价高昂,所以我们应该小心一点。我们可以通过推迟这项工作来减轻主线程上的负载:

connectedCallback() {let scope = this.parentNode;requestIdleCallback(() => {this.discover(scope);});
}

requestIdleCallback还没有得到普遍支持,但我们可以使用requestAnimationFrame作为后备:

let defer = window.requestIdleCallback || requestAnimationFrame;class AutoLoader extends HTMLElement {connectedCallback() {let scope = this.parentNode;defer(() => {this.discover(scope);});}// ...
}

现在我们可以继续实现缺少的load方法来动态注入