代理WebComponent的扩展HTMLElement的构造函数

ndu*_*ger 12 javascript proxy-pattern custom-element

因此,在我正在创建的使用自定义元素的库中,您显然需要在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());
Run Code Online (Sandbox Code Playgroud)

这个工程,不过,我想在这个类(所以笔者并没有给装饰添加到他们写的每一个组成部分)的实例自动注册自定义元素.这可以通过一个完成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());
Run Code Online (Sandbox Code Playgroud)

如何继续代理内部的继承链而不会丢失我实例化MyElement类的事实的上下文,以便它不会抛出Illegal Constructor异常?

小智 11

有2个问题:

  • new target()创建的LibElement实例,未注册为自定义元素.在这里你得到了Illegal Constructor错误.
  • 即使你注册LibElement结果DOM元素也会<lib-element>,因为你调用new target并且此时javascript不知道子类.

我发现的唯一方法是使用ReflectAPI来创建正确的对象实例.

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());
Run Code Online (Sandbox Code Playgroud)

我真的很喜欢代理构造函数的自定义元素自动注册的想法

  • 这是解决方案!你太棒了.我以为我不得不求助于`Reflect`,但我不确定代码中的时间和地点. (2认同)