Web Components 转换发生得太晚
Web Components conversion happens too late
在以下(简化的)代码中,第一个 Tester 实例使用预定义模板,而第二个实例使用直接编码的模板。插值适用于第二个,因为 html 包含正确的 HTML 代码。第一个 Tester 实例在插值期间仍然具有 的 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>
您的主要问题是:
s.innerHTML = s.innerHTML.replace(regex, this.stuff[key]);
s
是外DIV,不是<test-component>
内HTML
有效地销毁和重新创建<test-component>
每个键
因此 constructor()
总是将您的默认模板再次设置为 (element) 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)到empty 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>
在以下(简化的)代码中,第一个 Tester 实例使用预定义模板,而第二个实例使用直接编码的模板。插值适用于第二个,因为 html 包含正确的 HTML 代码。第一个 Tester 实例在插值期间仍然具有
<!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>
您的主要问题是:
s.innerHTML = s.innerHTML.replace(regex, this.stuff[key]);
s
是外DIV,不是<test-component>
内HTML
有效地销毁和重新创建<test-component>
每个键
因此 constructor()
总是将您的默认模板再次设置为 (element) 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)到empty 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>