代理扩展 HTMLElement 的 WebComponent 的构造函数
Proxy a WebComponent's constructor that extends HTMLElement
因此,在我创建的使用自定义元素的库中,您显然需要在实例化之前在 CustomElementsRegistry
中定义 class。
截至目前,正在使用装饰器解决此问题:
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());
这有效,但是,我想在实例化 class 时自动注册自定义元素(这样作者就不必添加装饰器他们编写的每个组件)。这可以通过 Proxy
.
来完成
不过,我的问题是,当我尝试在构造函数上使用代理并尝试 return 目标实例时,我仍然得到 Illegal Constructor
,好像该元素从未在注册表中定义过。
这显然与我在代理内部实例化 class 的方式有关,但我不确定如何否则。我的代码如下:
请 运行 最新 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
class 的上下文,这样它就不会抛出 Illegal Constructor
异常?
你在这里非常接近解决方案,唯一的问题是原生HTMLElement
不能用new
关键字实例化,必须通过document.createElement
创建。您可以重用您拥有的所有内容,只需替换代理中 construct
方法的 return 值:
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());
有 2 个问题:
new target()
创建了 LibElement
个实例,但未注册为自定义元素。在这里你得到 Illegal Constructor
错误。
- 即使您注册
LibElement
结果 DOM 元素将是 <lib-element>
,因为您调用 new target
而此时 javascript 不知道child class.
我发现的唯一方法是使用 Reflect
API 创建 object.
的正确实例
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());
而且我真的很喜欢这个为 auto-registration 个自定义元素代理构造函数的想法)
因此,在我创建的使用自定义元素的库中,您显然需要在实例化之前在 CustomElementsRegistry
中定义 class。
截至目前,正在使用装饰器解决此问题:
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());
这有效,但是,我想在实例化 class 时自动注册自定义元素(这样作者就不必添加装饰器他们编写的每个组件)。这可以通过 Proxy
.
不过,我的问题是,当我尝试在构造函数上使用代理并尝试 return 目标实例时,我仍然得到 Illegal Constructor
,好像该元素从未在注册表中定义过。
这显然与我在代理内部实例化 class 的方式有关,但我不确定如何否则。我的代码如下:
请 运行 最新 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
class 的上下文,这样它就不会抛出 Illegal Constructor
异常?
你在这里非常接近解决方案,唯一的问题是原生HTMLElement
不能用new
关键字实例化,必须通过document.createElement
创建。您可以重用您拥有的所有内容,只需替换代理中 construct
方法的 return 值:
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());
有 2 个问题:
new target()
创建了LibElement
个实例,但未注册为自定义元素。在这里你得到Illegal Constructor
错误。- 即使您注册
LibElement
结果 DOM 元素将是<lib-element>
,因为您调用new target
而此时 javascript 不知道child class.
我发现的唯一方法是使用 Reflect
API 创建 object.
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());
而且我真的很喜欢这个为 auto-registration 个自定义元素代理构造函数的想法)