具有代理的自定义元素具有、获取和设置

Custom elements with proxied has, get & set

我的目标是创建一个 class ObservableElement 扩展 HTMLElement 以便它可以用来定义自定义元素,例如:

customElements.define('an-observable-elem', class extends ObservableElement {
  construct() {
    super()
    ...
  }
  ...
})

关于任何基于 ObservableElement 的元素的事情是它应该有一些特定的行为 wrt 属性。

首先,'whatever' in myElem 应该总是 为真。换句话说,我想要实例上 has 的代理陷阱,它总是 returns true.

其次,设置和获取任何道具都应该有效。但是每当设置了未在元素或原型链中明确定义的道具时,我想发出一个名为 set${propname}detail: theValue.

的自定义事件

看来一定有办法使用es6-proxies。天真地,我第一次尝试:

class ObservableElement extends HTMLElement {
  constructor () {
    super()
    const vals = {}
    return Proxy(this, {
      has: _ => true,
      get: name => {
        if (name in this) return this[name]
        if (name in vals) return vals[name]
        return null
      },
      set: (name, value) => {
        if (name in this) {
          this[name] = value
          return
        }
        if (vals[name] === value) return
        vals[name] = value
        this.dispatchEvent(new CustomEvent(`set${name}`, {detail: value}))
      }
    })
  }
}

但这当然行不通。从构造函数返回代理并没有改变扩展 classes 的构造函数中的 this 值。我在 class、Object.setPrototypeOf(...) 等上尝试了各种代理 construct 的组合,但都无济于事。

如果有人了解这些东西如何组合在一起以实现我想要的东西,请向我解释一下,我将不胜感激。谢谢!

W3C Web 组件规范不支持您尝试执行的操作。

According to §2.4 of the W3C Web Components specification, step 10.6.2:

If observedAttributesIterable is not undefined, then set observedAttributes to the result of converting observedAttributesIterable to a sequence<DOMString>. Rethrow any exceptions from the conversion.

这发生在 customElements.define() 执行期间,这意味着您必须在定义 class 时为每个可能的字符串定义一个可迭代对象,以便拦截所有属性更改。

如果组件在该序列中找不到更改的 属性,它不会调用 attributeChangedCallback()

同样不可能 return 来自 constructor 的代理,因为 the restriction in §2.2:

A return statement must not appear anywhere inside the constructor body, unless it is a simple early-return (return or return this).