因此,在我正在创建的使用自定义元素的库中,您显然需要在CustomElementsRegistry
中定义该类,然后才能实例化它。
截至目前,这是由装饰者解决的:
class Component extends HTMLElement {
static register (componentName) {
return component => {
window.customElements.define(componentName, component);
return component;
}
}
}
@Component.register('my-element')
class MyElement extends Component { }
document.body.appendChild(new MyElement());
这可行,但是,我希望在实例化类时自动注册自定义元素(这样作者就不必将装饰器添加到他们编写的每个组件中)。这可以通过Proxy
完成。
我的问题是,当我尝试在构造函数上使用Proxy并尝试返回目标实例时,我仍然得到 Illegal Constructor
,就好像从未在注册表中定义过该元素。
这显然与我在代理中实例化类的方式有关,但我不确定如何做到这一点。我的代码如下:
请在最新的Chrome中运行
class Component extends HTMLElement {
static get componentName () {
return this.name.replace(/[A-Z]/g, char => `-${ char.toLowerCase() }`).substring(1);
}
}
const ProxiedComponent = new Proxy(Component, {
construct (target, args, extender) {
const { componentName } = extender;
if (!window.customElements.get(componentName)) {
window.customElements.define(componentName, extender);
}
return new target(); // culprit
}
});
class MyElement extends ProxiedComponent { }
document.body.appendChild(new MyElement());
如何继续代理内部的继承链,而不会丢失我实例化MyElement
类的事实的上下文,这样它就不会抛出 Illegal Constructor
例外?
答案 0 :(得分:11)
有两个问题:
new target()
已创建LibElement
实例,该实例未注册为自定义元素。在这里你得到Illegal Constructor
错误。LibElement
生成的DOM元素为<lib-element>
,也会导致您调用new target
,此时javascript根本不知道子类。我找到的唯一方法是使用Reflect
API创建正确的对象实例。
class LibElement extends HTMLElement {
static get componentName () {
return this.name.replace(/[A-Z]/g, char => `-${ char.toLowerCase() }`).substring(1);
}
}
const LibElementProxy = new Proxy(LibElement, {
construct (base, args, extended) {
if (!customElements.get(extended.componentName)) {
customElements.define(extended.componentName, extended);
}
return Reflect.construct(base, args, extended);
}
});
class MyCustomComponent extends LibElementProxy {}
class MyCustomComponentExtended extends MyCustomComponent {}
document.body.appendChild(new MyCustomComponent());
document.body.appendChild(new MyCustomComponentExtended());
&#13;
我真的很喜欢代理构造函数的自定义元素自动注册的想法
答案 1 :(得分:1)
此处非常接近解决方案,唯一的问题是无法使用HTMLElement
关键字对原始new
进行实例化,并且必须通过document.createElement
创建。{1}}。您可以重用所有内容,只替换代理中construct
方法的返回值:
class Component extends HTMLElement {
static get componentName() {
return this.name.replace(/[A-Z]/g, char => `-${ char.toLowerCase() }`).substring(1);
}
}
const ProxiedComponent = new Proxy(Component, {
construct(target, arguments, extender) {
const {
componentName
} = target;
if (!window.customElements.get(componentName)) {
window.customElements.define(componentName, extender);
}
return document.createElement(target.componentName); // Properly constructs the new element
}
});
class MyElement extends ProxiedComponent {}
document.body.appendChild(new MyElement());