【问题标题】:Unable to trap accessor calls on customElements using Proxy?无法使用代理捕获 customElements 上的访问器调用?
【发布时间】:2026-01-20 09:05:02
【问题描述】:

我正在使用 customElements.define 注册一些自定义元素,并希望在成员访问器上自动设置陷阱,以便在它们发生变化时发出事件

class State extends HTMLElement {
    public someValue = 1;

    public constructor() {
        super();
        console.log('State constructor');
    }
}

const oProxy = new Proxy(State, {
    get(target, prop: string) {
        console.log(`GET trap ${prop}`);
        return Reflect.get(target, prop);
    },
    set(target, prop: string, value: any) {
        console.log(`SET trap ${prop}`);
        return Reflect.set(target, prop, value);
    }
});

customElements.define('my-state', oProxy);

const oStateEl = document.querySelector('my-state');
console.log(oStateEl.someValue);
console.log(oStateEl.someValue = 2);
console.log(oStateEl.someValue);

我的浏览器似乎对上面的代码没有问题,并且在设置元素时我可以看到一些陷阱输出

GET trap prototype
GET trap disabledFeatures
GET trap formAssociated
GET trap prototype

但是当我手动获取/设置值时,不会触发陷阱。 这甚至可能吗?

【问题讨论】:

  • 不,我不明白这怎么可能。你到底在做什么?

标签: javascript proxy custom-element


【解决方案1】:

我最终做的是将所有成员变量值移动到一个私有对象,并在自定义元素像这样安装在 DOM 上后立即为每个成员动态定义一个 getter/setter...

//
class State extends HTMLElement {

    protected _data: object = {}

    public connectedCallback() {
        // Loop over member vars
        Object.getOwnPropertyNames(this).forEach(sPropertyKey => {
            // Ignore private
            if(sPropertyKey.startsWith('_')) {
                return;
            }

            // Copy member var to data object
            Reflect.set(this._data, sPropertyKey, Reflect.get(this, sPropertyKey));

            // Remove member var
            Reflect.deleteProperty(this, sPropertyKey);

            // Define getter/setter to access data object
            Object.defineProperty(this, sPropertyKey, {
                set: function(mValue: any) {
                    console.log(`setting ${sPropertyKey}`);
                    Reflect.set(this._data, sPropertyKey, mValue);
                },

                get: function() {
                    return this._data[sPropertyKey];
                }
            });
        });
    }

}

// 
class SubState extends State {

    public foobar = 'foobar_val';
    public flipflop = 'flipflop_val';

    public SubStateMethod() { }

}

//
window.customElements.define('sub-state', SubState);

//
const oState = document.querySelector('sub-state') as SubState;
oState.foobar = 'foobar_new_val';

这样我仍然可以正常获取/设置对象的值,打字稿很高兴成员变量存在,并且我可以在访问成员时触发自定义事件 - 同时允许自定义元素存在于 DOM 的标记中准备好了

【讨论】: