DOMContentLoaded 与 Web 组件有什么关系?

问题描述 投票:0回答:3

只有在 DOM 准备就绪后才开始操作它是古老的常识,我们可以确定所有元素都可用,在后 jQuery 时代,我们都为此使用

DOMContentLoaded
事件。

现在 web 组件(尤其是自治自定义元素的形式)倾向于创建自己的 HTML,通常在

connectedCallback()
生命周期方法中。

第一题:

DOMContentLoaded
与(自主)自定义元素有何关系?只有在所有组件 connectedCallbacks 完成后事件才会发生吗?如果不是,我如何确保某些代码仅在 Web 组件完成初始化后执行?

第二个问题,完全相关:

Web 组件如何与

defer
元素的
script
属性相关?

javascript web-component deferred-loading domcontentloaded
3个回答
2
投票

我不喜欢网络组件,但我会说......一点也不喜欢。

您的组件由您的脚本定义,但在此之前,浏览器仍将像往常一样解析标记并执行所有同步脚本,并在完成时触发 DOMContentLoaded。

因此,如果您确实在 before DOMContentLoaded 事件触发之前同步定义了 CustomElements,那么您的元素

connectedCallback
将被触发(因为它不是一个事件,它是一个回调,并且被称为 synchronously)。

if (window.customElements) {

  addEventListener('DOMContentLoaded', e => console.log('DOM loaded'));

  class MyCustom extends HTMLElement {
    connectedCallback() {
      console.log('Custom element added to page.');
    }
  }

  customElements.define('my-custom', MyCustom);
  console.log('Just defined my custom element')

} else {
  console.log("your browser doesn't have native support");
}
<my-custom></my-custom>

但是如果你确实等待 DOMContentLoaded 事件,那么......回调将在之后触发。

if (window.customElements) {

  addEventListener('DOMContentLoaded', e => console.log('DOM loaded'));

  class MyCustom extends HTMLElement {
    connectedCallback() {
      console.log('Custom element added to page.');
    }
  }

  setTimeout(()=> customElements.define('my-custom', MyCustom), 2000);

} else {
  console.log("your browser doesn't have native support");
}
<my-custom></my-custom>

但是 DOMContentLoaded 绝不会等待所有脚本的同步执行结束,就像您那里没有任何 CustomElement 一样。


关于您的最后一个问题,正如in the docs about the

defer
attribute,具有此类属性的脚本将在before DOMContentLoaded fires.


2
投票

它们在不同的轴上。

DOMContentLoaded
是关于解析初始 HTML 文档,因此是下载的原始“文件”。组件在定义时准备就绪。
我也不熟悉这个主题,所以只是修改了 MDN 示例以通过按下按钮完全分离这两个事件。我假设新组件的定义随时都可能发生,事实确实如此。
MDN 示例是 GitHub 上的,链接自
CustomElementRegistry.define
(以及其他各种页面)。他们在 GitHub IO 上也有一个实时变体,当然这只是原始示例。 把完整的例子放在这里感觉没希望了,所以这只是修改后的HTML:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Pop-up info box — web components</title> </head> <body id="body"> <h1>Pop-up info widget - web components</h1> <form> <div> <label for="cvc">Enter your CVC <popup-info img="img/alt.png" text="Your card validation code (CVC) is an extra security feature — it is the last 3 or 4 numbers on the back of your card."></label> <input type="text" id="cvc"> </div> </form> <!--<script src="main.js"></script>--> <script> function test(){ var x=document.createElement("script"); x.src="main.js"; document.getElementById("body").appendChild(x); } customElements.whenDefined("popup-info").then(function(){alert("popup-info");}); document.addEventListener("DOMContentLoaded",function(){alert("DOMContentLoaded")}); </script> <button onclick="test()">Test</button> </body> </html>

所以

main.js

不会自动加载,只有按下按钮后,它仍然有效。 “DOMContentLoaded”弹出窗口已经出现很长时间了。然而,有一个 
CustomElementRegistry.whenDefined()
 是有耐心的,只有在自定义元素被定义后才会触发。我认为这就是您可以使用的,也许可以从 DOMContentLoaded 订阅它,因此您的最终事件将保证在 DOM 和自定义元素都准备就绪时发生。缺点是您必须知道您正在等待的自定义元素的名称。 (未经测试的假设,但基于 
https://html.spec.whatwg.org/multipage/scripting.html 上的调度图,可能会使 whenDefined()
DOMContentLoaded
 之前发生。顺便说一下:图表还显示了 
defer
 的作用,所以如果您将 
whenDefined()
 放入延迟脚本中,回调将在 
DOMContentLoaded
 之后发生,或者至少我是这么认为的)


0
投票

对于您的第一个问题: 不用担心外页——只需担心组件本身。但是,由于组件的嵌套内容和 shadowDOM 准备就绪时没有可用的方法(请参阅WICG Web Components Issue #809MDN connectedCallback

),在connectedCallback
 中,您可以将代码放在像这样的“DOMContentLoaded”事件监听器

connectedCallback() { addEventListener('DOMContentLoaded', () => { // Your code, such as both `this.children` and `this.shadowRoot.querySelector()` }); }
或者,您可以使用非常冗长的 

MutationObserver 和去抖功能;但后者是过度的。

对于你的第二个问题:虽然差异仍然有效,但我会避免它,以便浏览器可以尽快处理组件 DOM,而不是等到整个页面文档准备好。

© www.soinside.com 2019 - 2024. All rights reserved.