首页 > 解决方案 > Web 组件转换发生得太晚

问题描述

在以下(简化的)代码中,第一个 Tester 实例使用预定义的模板,而第二个使用直接编码的模板。插值适用于第二个,因为 html 包含正确的 HTML 代码。在插值期间,第一个 Tester 实例仍然具有 <test-component> 的 innerHTML。如何更改代码概念以插入第一个示例?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Web Components Demo</title>
</head>
<body>
    <div id="tc"></div>
    <div id="tc2"></div>
    <script>
        class TestComponent extends HTMLElement {
            constructor () {
                super();
                this.attachShadow({mode: "open"});
                this.shadowRoot.appendChild(this.createTemplate().content.cloneNode(true));
            }
            createTemplate () {
                const template = document.createElement("template");
                template.innerHTML = "<h1>{{title}}</h1><p>{{text}}</p>";
                return template;
            }
        }
        window.customElements.define("test-component", TestComponent);

        class Tester {
            constructor ({selector, stuff, template}) {
                this.selector = Array.from(document.querySelectorAll(selector));
                this.template = template || null;
                this.stuff = stuff;
                this.render();
                this.interpolate();
            }
            render () {
                this.selector.forEach(s => {if (this.template) s.innerHTML = this.template});
            }
            interpolate () {
                this.selector.forEach(s => {
                    for (let key in this.stuff) { 
                        const regex = new RegExp(`{{ *${key} *}}`, "g");       
                        s.innerHTML = s.innerHTML.replace(regex, this.stuff[key]);;
                    } 
                });
            }
        }

        new Tester ({
            selector: "#tc",
            stuff: {title: "Test Title", text: "Lorem ipsum dolor sit amet"},
            template: "<test-component>"
        });

        new Tester ({
            selector: "#tc2",
            stuff: {title: "Title that works", text: "Lorem ipsum dolor sit amet"},
            template: "<h1>{{title}}</h1><p>{{text}}</p>"
        })
    </script>
</body>
</html>

标签: javascriptinterpolationweb-component

解决方案


你的主要问题是:

s.innerHTML = s.innerHTML.replace(regex, this.stuff[key]);

s是外部 DIV,而不是内部<test-component>HTML

有效地销毁和重新创建 <test-component> 每个键

因此,constructor()总是将您的默认模板再次设置为(元素)innerHTML:

控制台记录您在第一个示例中运行的代码:

笔记

constructor () {
    super();
    this.attachShadow({mode: "open"});       
    this.shadowRoot.appendChild(this.createTemplate().content.cloneNode(true));
}
createTemplate () {
    const template = document.createElement("template");
    template.innerHTML = "<h1>{{title}}</h1><p>{{text}}</p>";
    return template;
}

有点臃肿:您正在模板内创建HTML,然后模板的克隆内容(即:HTML)添加到的shadowRoot innerHTML

constructor () {
    super() // returns 'this'
       .attachShadow({mode: "open"}) // returns shadowRoot
       .innerHTML = "<h1>{{title}}</h1><p>{{text}}</p>";
}

当您需要重新使用原始(模板)时,您只需克隆(模板)

解决方案:将 {{}} 解析移到元素内部

<test-component id=One></test-component>
<test-component id=Two></test-component>
<script>
  window.customElements.define("test-component", class extends HTMLElement {
    constructor() {
      super().attachShadow({ mode: "open" });
      this.setTemplate(`<b>{{title}}</b> {{text}}`);
    }
    setTemplate(html, data = {}) {
      this.shadowRoot.innerHTML = html;
      this.parse(data);
    }
    parse(data) {
      let html = this.shadowRoot.innerHTML;
      for (let key in data) {
        const regex = new RegExp(`{{${key}}}`, "g");
        html = html.replace(regex, data[key]);
      }
      this.shadowRoot.innerHTML = html;
    }
  });
  One.parse({
    title: "Test Title",
    text: "Lorem ipsum dolor sit amet"
  })
  Two.setTemplate('<h4>{{title}}<h4>{{subtitle}}', {
    title: "Test Two",
    subtitle: "a Sub title"
  })
  let Three = document.createElement('test-component');
  Three.parse({//parsed IN shadowDOM innerHTML!
    title: "Title Three",
    text: "text three"
  })
  document.body.append(Three)
</script>


推荐阅读