假设我有一个对话框组件,比如
class ModalDialog extends HTMLElement {
constructor(){
super()
this._shadow = this.attachShadow({mode: 'closed'})
}
connectedCallback(){
const template = `
<style>
... lots of style that doesn't matter to this question ...
</style>
<div class="dialog">
<div class="dialog-content">
<div class="dialog-header">
<slot name="header"></slot>
<span class="close">×</span>
</div>
<div class="dialog-body"><slot name="body"></slot></div>
<div class="dialog-footer"><slot name="footer"></slot></div>
</div>
</div>
`
this._shadow.innerHTML = template
this._shadow.querySelector('.close').onclick = () => this.hide()
const dialog = this._shadow.querySelector('.dialog')
dialog.onclick = evt => {
if(evt.target == dialog){ //only if clicking on the overlay
this.hide()
}
}
this.hide()
}
show() {
this.style.display = 'block'
}
hide(){
this.style.display = 'none'
}
}
window.customElements.define('modal-dialog', ModalDialog)现在假设我想创建专用的对话框...例如,允许用户选择图像的图像。
我可以这样做
import {} from './modal-dialog.js'
class ImageSelector extends HTMLElement {
constructor(){
super()
this._shadow = this.attachShadow({mode: 'closed'})
}
connectedCallback(){
const template = `
<style>
... more style that doesn't matter ...
</style>
<modal-dialog>
<div slot="header"><h3>Select Image</h3></div>
<div slot="body">
... pretend there's some fancy image selection stuff here ...
</div>
</modal-dialog>
`
this._shadow.innerHTML = template
}
show(){
this._shadow.querySelector('modal-dialog').show()
}
hide(){
this._shadow.querySelector('modal-dialog').hide()
}
}
window.customElements.define('image-selector', ImageSelector)但是我不喜欢那里的show和hide方法。
另一种选择是从对话框而不是从HTMLElement继承...
import {} from './modal-dialog.js'
class ImageSelector extends customElements.get('modal-dialog'){
constructor(){
super()
}
connectedCallback(){
... now what? ...
}
}
window.customElements.define('image-selector', ImageSelector)但如果我这样做了,我实际上该如何填补空缺呢?
天真的方法当然是使用_shadow并将其放入插槽的内部html中,但我有一种感觉,这不是一种方法。
发布于 2021-03-08 11:57:26
TLDR;不可能同时使用继承和组合。
长长的答案:
您实际上混合了两个截然不同但不同的概念:
通常,在Web编程中,由于Composition提供的松散耦合,它总是比继承更受欢迎。
在这种情况下,你实际上想要使用模板,而不是实际覆盖它的。因此,组合在这里是一个更好的选择。但这也意味着您实际上必须编写更多的样板代码,即show和hide方法的包装器实现。
从理论上讲,发明继承是为了促进代码重用和避免重复代码,但与组合相比,这是一种成本。
发布于 2021-03-08 20:55:25
Harshals的回答提供了不错的信息;以下是userland代码
除非您正在执行SSR,否则读取的第一个文件是HTML文件。
因此,将模板内容放在那里,让one generic Modal Web组件读取模板
<template id="MODAL-DIALOG">
<style>
:host { display: block; background: lightgreen; padding:.5em }
[choice]{ cursor: pointer }
</style>
<slot></slot>
<button choice="yes">Yes</button>
<button choice="no" >No</button>
</template>
<template id="MODAL-DIALOG-IMAGES">
<style>
:host { background: lightblue; } /* overrule base template CSS */
img { height: 60px; }
button { display: none; } /* hide stuff from base template */
</style>
<h3>Select the Framework that is not Web Components friendly</h3>
<img choice="React" src="https://image.pngaaa.com/896/2507896-middle.png">
<img choice="Vue" src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/1184px-Vue.js_Logo_2.svg.png">
<img choice="Svelte" src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1b/Svelte_Logo.svg/1200px-Svelte_Logo.svg.png">
<slot><!-- remove slot from BaseTemplate, then this slot works --></slot>
</template>
<modal-dialog>Standard Modal</modal-dialog>
<modal-dialog template="-IMAGES">Images Modal</modal-dialog>
<script>
document.addEventListener("modal-dialog", (evt) => {
alert(`You selected: ${evt.detail.getAttribute("choice")}`)
});
customElements.define('modal-dialog', class extends HTMLElement {
constructor() {
let template = (id="") => {// if docs say "use super() first" then docs are wrong
let templ = document.getElementById(this.nodeName + id);
if (templ) return templ.content.cloneNode(true);
else return []; // empty content for .append
}
super().attachShadow({mode:"open"})
.append( template(),
template( this.getAttribute("template") ));
this.onclick = (evt) => {
let choice = evt.composedPath()[0].hasAttribute("choice");
if (choice)
this.dispatchEvent(
new CustomEvent("modal-dialog", {
bubbles: true,
composed: true,
detail: evt.composedPath()[0]
})
);
// else this.remove();
}
}
connectedCallback() {}
});
</script>
笔记
<TEMPLATES>包装在JS字符串中
<script>嗯,你可以..。但它在全局范围内运行,而不是在组件范围内运行
<SLOT>,您可能需要使用代码从底板中移除不需要的插槽
- Good Components do a handful of things very good.
- Bad Components try to do everything... create another Componenthttps://stackoverflow.com/questions/66522470
复制相似问题