使用 lit-html 呈现的嵌套 HTMLElement 覆盖父模板

问题描述

我有两个最小的 HTMLElement:一个 AppRoot一个 SubElement。元素的 innerHTML 是通过 lit-html 的 renderhtml 模板函数生成的。

AppRoot 的 HTML 模板是一个 div,其中有两个段落:一个显示 message 属性的文本,另一个实例化一个 SubElement 并通过它是一个字符串。

SubElement 的 HTML 模板只是传递的字符串。

我希望呈现的 HTML 看起来像这样:

<div>
    <p>AppRoot's message</p>
    <p>The string passed to SubElement</p>
</div>

但实际上只是SubElement的渲染模板:

The string passed to SubElement

为什么 SubElement 的模板在渲染时会替换 AppRoot 的模板?我已经尝试过更改标签(用 div、段落括起来),但无济于事。

您可以在下面找到源代码

<!DOCTYPE html>
<html lang="en">
<head>
    <Meta charset="UTF-8">
    <Meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script src="./index.js" type="module"></script>
    <app-root></app-root>
</body>
</html>
import { render,html } from './node_modules/lit-html/lit-html.js';


class SubElement extends HTMLElement {

    constructor(message) {
        super();
        this.message = message;
    }

    connectedCallback() {
        this.innerHTML = render(this.template(),document.body);;
    }

    template() {
        return html`
            ${this.message}
        `;
    }

}

customElements.define('sub-element',SubElement);


class AppRoot extends HTMLElement {

    constructor() {
        super();
        this.message = "I am root!";
    }

    connectedCallback() {
        this.innerHTML = render(this.template(),document.body);
    }

    template() {
        return html`
            <div>
                <p>${this.message}</p>
                <p>${new SubElement("I am not root!")}</p>
            </div>
        `;
    }

}

customElements.define('app-root',AppRoot);

解决方法

render 的第二个参数是模板应该被渲染的容器。每个组件当前都在将模板结果渲染到文档正文并覆盖之前渲染的结果。

connectedCallback() {
    this.innerHTML = render(this.template(),document.body);
}

您应该考虑在组件本身内使用 shadow DOM 或渲染。

connectedCallback() {
    render(this.template(),this);
}