如何在Lit组件中检测另一懒加载Web Component的挂载状态?
检测Lit/Web Component的挂载状态及跨组件检测方案
问题描述
我需要在一个Lit开发的自定义组件中,检测另一个独立懒加载的自定义Web Component(如<my-component-one>和<my-component-two>)是否已连接/挂载,并监听其挂载时机。目前已实现两种方式:
- 检查目标元素的
shadowRoot属性 - 在目标组件的
connectedCallback中触发自定义事件并监听
现有代码示例:
@customElement('my-component-one') export class MyComponentOne extends LitElement { connectedCallback() { super.connectedCallback() if (document.querySelector('my-component-two')?.shadowRoot) { console.log('🎉 component two mounted, it loaded before component one') } else { window.addEventListener('my-component-two:mounted', () => { console.log('🎉 component two mounted, it loaded after component one') }) } } } // my-component-two的connectedCallback中 this.dispatchEvent(new CustomEvent('my-component-two:mounted', { bubbles: true, composed: true }))
希望找到无需修改目标组件(即不用触发自定义事件)的更简便实现方式,尝试过监听connectedCallback相关原生事件、mounted/ready/load等事件均未成功。
无需自定义事件的解决方案
1. 结合customElements.whenDefined()与元素监听
customElements.whenDefined()可以监听自定义元素的类加载完成,之后再等待元素挂载到DOM并完成渲染,无需修改目标组件:
@customElement('my-component-one') export class MyComponentOne extends LitElement { async connectedCallback() { super.connectedCallback() // 等待my-component-two的类定义完成 await customElements.whenDefined('my-component-two') // 等待元素出现在DOM中 const targetElem = await this.waitForElement('my-component-two') // 等待元素的shadowRoot创建完成(Lit首次渲染后生成) await this.waitForShadowRoot(targetElem) console.log('🎉 component two mounted and rendered') } // 监听DOM中目标元素的出现 waitForElement(selector) { return new Promise(resolve => { const existingElem = document.querySelector(selector) if (existingElem) return resolve(existingElem) const observer = new MutationObserver(() => { const elem = document.querySelector(selector) if (elem) { resolve(elem) observer.disconnect() } }) observer.observe(document.body, { childList: true, subtree: true }) }) } // 循环检查shadowRoot是否存在 waitForShadowRoot(element) { return new Promise(resolve => { const check = () => { element.shadowRoot ? resolve() : requestAnimationFrame(check) } check() }) } }
2. 使用MutationObserver监听DOM与ShadowRoot变化
通过MutationObserver可以同时监听目标元素的DOM添加和shadowRoot创建,逻辑更集中:
@customElement('my-component-one') export class MyComponentOne extends LitElement { connectedCallback() { super.connectedCallback() this.setupComponentObserver() } setupComponentObserver() { // 先检查元素是否已存在并挂载 const target = document.querySelector('my-component-two') if (target?.shadowRoot) { this.handleMounted() return } // 监听DOM中新增的节点 const domObserver = new MutationObserver((mutations) => { for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node.tagName === 'MY-COMPONENT-TWO') { // 监听该元素的shadowRoot创建 const shadowCheck = setInterval(() => { if (node.shadowRoot) { this.handleMounted() clearInterval(shadowCheck) domObserver.disconnect() } }, 50) } } } }) domObserver.observe(document.body, { childList: true, subtree: true }) } handleMounted() { console.log('🎉 component two mounted') } }
3. 利用Lit专属的updateComplete Promise(推荐)
如果目标组件是Lit开发的,可以直接使用Lit提供的updateComplete Promise,它会在组件首次渲染完成后resolve,比检查shadowRoot更可靠:
@customElement('my-component-one') export class MyComponentOne extends LitElement { async connectedCallback() { super.connectedCallback() await customElements.whenDefined('my-component-two') const targetElem = await this.waitForElement('my-component-two') // 等待Lit组件完成首次更新渲染 await targetElem.updateComplete console.log('🎉 component two fully mounted and rendered') } waitForElement(selector) { return new Promise(resolve => { const existingElem = document.querySelector(selector) if (existingElem) return resolve(existingElem) const observer = new MutationObserver(() => { const elem = document.querySelector(selector) if (elem) { resolve(elem) observer.disconnect() } }) observer.observe(document.body, { childList: true, subtree: true }) }) } }
方案说明
- 方法1和2是通用Web Component方案,不依赖Lit特性,适用于所有自定义元素
- 方法3是Lit专属方案,能精准判断组件是否完成渲染(shadowRoot存在不代表渲染完成),是Lit项目的最优选择
内容的提问来源于stack exchange,提问作者sgarcia.dev
相关产品推荐
相关产品推荐

