如果 JavaScript 中的所有内容都来自原型,我想有某种方法可以在不使用类的情况下创建 Web 组件。我从来没有找到任何关于它的文章,我不知道这是因为这是一个不好的做法还是因为它不可能。
是的,无需使用
class
语法即可完成。诀窍是使用 Reflect.construct
来代替对 super
的调用。这是一个小示例片段,包含一个工厂 (createCustomElementFactory
),用于创建使用 customElements.define
注册自定义元素所需的“类”。
要使用代码,请分叉此 Stackblitz 项目。
const myCustomElementCreator = createCustomElementFactory();
const connectedCallback = function() {
const isCustom = this.getAttribute(`is`);
if (!isCustom) {
this.style.display = `block`;
this.style.padding = `2px 5px`;
this.style.border = `1px solid #999`;
}
this.insertAdjacentHTML(`afterbegin`, `<div>I am <i><${
this.tagName.toLowerCase()}${
isCustom ? ` is="${isCustom}"` : ``}></i></div>`);
console.log(`We just connected the ${
isCustom ? `custom` : `autonomous`} element '${
this.tagName.toLowerCase()}${isCustom ? `[is="${isCustom}"]` : ``}'`);
};
const myBrandNewElemClass = myCustomElementCreator({ connectedCallback } );
const myBrandNewParagraphClass = myCustomElementCreator({
connectedCallback,
forElem: HTMLParagraphElement });
// register 2 elements
customElements.define( "my-brand-new-element", myBrandNewElemClass );
customElements.define(
"my-brand-new-paragraph",
myBrandNewParagraphClass,
{ extends: `p` } );
document.body.insertAdjacentHTML(
`beforeend`,
`<my-brand-new-element>Hello world!</my-brand-new-element>`
);
document.body.insertAdjacentHTML(
`beforeend`,
`<p is="my-brand-new-paragraph">Hello paragraph!</p>`
);
function createCustomElementFactory() {
const paramsPlaceholder = ({
get all() {
return {
connectedCallback,
disconnectedCallback,
adoptedCallback,
attributeChangedCallback,
observedAttributes,
forElem } = {}
}
});
function CustomElementConstructorFactory(params = paramsPlaceholder.all) {
const elemProto = params.forElem?.prototype instanceof HTMLElement
? params.forElem : HTMLElement;
function CustomElementConstructor() {
if (elemProto !== HTMLElement) {
self = Reflect.construct( params.forElem, [], CustomElementConstructor );
return self;
}
return Reflect.construct( HTMLElement, [], CustomElementConstructor );
}
CustomElementConstructor.prototype = elemProto.prototype;
CustomElementConstructor.observedAttributes = params.observedAttributes;
Object.entries( params ).forEach( ([name, cb]) => {
if (cb && cb instanceof Function) {
CustomElementConstructor.prototype[name] = cb; }
} );
return CustomElementConstructor;
}
return (params = paramsPlaceholder.all) => CustomElementConstructorFactory(params);
}