如果 属性 在定义元素之前设置了相同的名称,则 CustomElement 的初始化会删除 class 方法(get/set 也)
Initialisation of CustomElement removes class methods(get/set also) if property with same name set before element defined
自定义元素可以异步初始化,例如在延迟加载其定义后 class。
让我们看一下这个例子,我们在 DOM 中有一个尚未初始化的元素:
<component-with-async-init></component-with-async-init>
在初始化之前我们设置了这个元素的value
属性:
querySelector('component-with-async-init').value = "GETTERS SETTERS ARE KILLED"
然后我们通过将其定义为自定义元素来初始化该元素。如您所见,它的定义 class 对于 value
属性 有 getter 和 setter。
class ComponentWithAsyncInit extends HTMLElement{
static get is() {
return 'component-with-async-init'
}
get value(){
console.log("Getting value")
return "VALUE FROM GETTER"
}
set value(v){
console.log("Setting value")
}
}
window.customElements.define(ComponentWithAsyncInit.is, ComponentWithAsyncInit);
getter 和 setters(或任何方法)现在被杀死,如您所见:
querySelector('component-with-async-init').value //"GETTERS SETTERS ARE KILLED"
我在最新的 Safari、Firefox 和 Chrome 中观察到此行为,其他未测试。此问题会影响 Polymer 和 Lit-Element 库以及纯自定义元素。
似乎它在每个浏览器中的工作方式都相同。
问题:这真的是预期的行为吗?如果在元素定义之前设置了 属性,则 Getters 和 Setters 以及任何方法都会被清除。如果是,什么是解决方法,因为我们通常无法控制何时设置元素值
片段:
document.querySelector('component-with-async-init').value = "SETTERS GETTERS KILLED"
document.querySelector('component-with-async-init').getSomeValue = "GET SOME VALUE KILLED"
class ComponentWithAsyncInit extends HTMLElement{
static get is() {
return 'component-with-async-init'
}
get value(){
console.log("Getting value")
return "VALUE FROM GETTER"
}
set value(v){
console.log("Setting value")
}
getSomeValue(){
return "SOME VALUE"
}
}
window.customElements.define(ComponentWithAsyncInit.is, ComponentWithAsyncInit);
console.log("getSomeValue method:")
console.log(document.querySelector('component-with-async-init').getSomeValue)
console.log("Reading value:")
console.log(document.querySelector('component-with-async-init').value)
<component-with-async-init></component-with-async-init>
这确实是预料之中的。 HTML specs 询问何时创建 CustomElement UAs
- Perform
element.[[SetPrototypeOf]](prototype)
.
这是 the one from ECMAScript standards,它只设置 element
将从中继承的 [[Prototype]]
。
这样做不会覆盖自己的属性值。
const elem = { a: "my own a" };
Object.setPrototypeOf(elem, { a: "proto's a", b: "proto's b" });
console.log(elem.a) // "my own a"
console.log(elem.b) // "proto's b"
您可以尝试通过再次显式设置已被覆盖的属性来解决该问题,然后重新设置值以便调用设置器,
document.querySelector('component-with-async-init').value = "SETTERS GETTERS KILLED";
document.querySelector('component-with-async-init').getSomeValue = "GET SOME VALUE KILLED";
class ComponentWithAsyncInit extends HTMLElement {
constructor() {
super();
// first grab the properties that might already have been set
const ownProps = Object.entries(this);
// our class's prototype
const proto = ComponentWithAsyncInit.prototype;
// get all properties descriptors
const proto_desc = Object.getOwnPropertyDescriptors(proto);
ownProps.forEach(([key, value]) => {
// got overriden
if (key in proto_desc) {
// apply the one from our class
Object.defineProperty(this, key, proto_desc[key]);
// set again the own property (call setters)
this[key] = value;
}
});
}
static get is() {
return 'component-with-async-init'
}
get value() {
console.log("Getting value")
return "VALUE FROM GETTER"
}
set value(v) {
console.log("Setting value")
}
getSomeValue() {
return "SOME VALUE"
}
}
window.customElements.define(ComponentWithAsyncInit.is, ComponentWithAsyncInit);
customElements.upgrade(document.querySelector('component-with-async-init'))
console.log("getSomeValue method:") // note that this will get overidden anyway
console.log(document.querySelector('component-with-async-init').getSomeValue)
console.log("Reading value:")
console.log(document.querySelector('component-with-async-init').value)
<component-with-async-init></component-with-async-init>
但这听起来真的很难看,并且不会在初始化之前阻止诸如方法调用之类的事情。
所以最好在使用这些元素之前等待您的元素注册。
customElements.whenDefined("component-with-async-init")
.then(() => {
document.querySelector('component-with-async-init').value = "SETTERS GETTERS KILLED"
document.querySelector('component-with-async-init').getSomeValue = "GET SOME VALUE KILLED"
console.log("getSomeValue method:") // note that this will get overidden anyway
console.log(document.querySelector('component-with-async-init').getSomeValue)
console.log("Reading value:")
console.log(document.querySelector('component-with-async-init').value)
});
class ComponentWithAsyncInit extends HTMLElement{
static get is() {
return 'component-with-async-init'
}
get value(){
console.log("Getting value")
return "VALUE FROM GETTER"
}
set value(v){
console.log("Setting value")
}
getSomeValue(){
return "SOME VALUE"
}
}
window.customElements.define(ComponentWithAsyncInit.is, ComponentWithAsyncInit);
<component-with-async-init></component-with-async-init>
自定义元素可以异步初始化,例如在延迟加载其定义后 class。
让我们看一下这个例子,我们在 DOM 中有一个尚未初始化的元素:
<component-with-async-init></component-with-async-init>
在初始化之前我们设置了这个元素的value
属性:
querySelector('component-with-async-init').value = "GETTERS SETTERS ARE KILLED"
然后我们通过将其定义为自定义元素来初始化该元素。如您所见,它的定义 class 对于 value
属性 有 getter 和 setter。
class ComponentWithAsyncInit extends HTMLElement{
static get is() {
return 'component-with-async-init'
}
get value(){
console.log("Getting value")
return "VALUE FROM GETTER"
}
set value(v){
console.log("Setting value")
}
}
window.customElements.define(ComponentWithAsyncInit.is, ComponentWithAsyncInit);
getter 和 setters(或任何方法)现在被杀死,如您所见:
querySelector('component-with-async-init').value //"GETTERS SETTERS ARE KILLED"
我在最新的 Safari、Firefox 和 Chrome 中观察到此行为,其他未测试。此问题会影响 Polymer 和 Lit-Element 库以及纯自定义元素。 似乎它在每个浏览器中的工作方式都相同。
问题:这真的是预期的行为吗?如果在元素定义之前设置了 属性,则 Getters 和 Setters 以及任何方法都会被清除。如果是,什么是解决方法,因为我们通常无法控制何时设置元素值
片段:
document.querySelector('component-with-async-init').value = "SETTERS GETTERS KILLED"
document.querySelector('component-with-async-init').getSomeValue = "GET SOME VALUE KILLED"
class ComponentWithAsyncInit extends HTMLElement{
static get is() {
return 'component-with-async-init'
}
get value(){
console.log("Getting value")
return "VALUE FROM GETTER"
}
set value(v){
console.log("Setting value")
}
getSomeValue(){
return "SOME VALUE"
}
}
window.customElements.define(ComponentWithAsyncInit.is, ComponentWithAsyncInit);
console.log("getSomeValue method:")
console.log(document.querySelector('component-with-async-init').getSomeValue)
console.log("Reading value:")
console.log(document.querySelector('component-with-async-init').value)
<component-with-async-init></component-with-async-init>
这确实是预料之中的。 HTML specs 询问何时创建 CustomElement UAs
- Perform
element.[[SetPrototypeOf]](prototype)
.
这是 the one from ECMAScript standards,它只设置 element
将从中继承的 [[Prototype]]
。
这样做不会覆盖自己的属性值。
const elem = { a: "my own a" };
Object.setPrototypeOf(elem, { a: "proto's a", b: "proto's b" });
console.log(elem.a) // "my own a"
console.log(elem.b) // "proto's b"
您可以尝试通过再次显式设置已被覆盖的属性来解决该问题,然后重新设置值以便调用设置器,
document.querySelector('component-with-async-init').value = "SETTERS GETTERS KILLED";
document.querySelector('component-with-async-init').getSomeValue = "GET SOME VALUE KILLED";
class ComponentWithAsyncInit extends HTMLElement {
constructor() {
super();
// first grab the properties that might already have been set
const ownProps = Object.entries(this);
// our class's prototype
const proto = ComponentWithAsyncInit.prototype;
// get all properties descriptors
const proto_desc = Object.getOwnPropertyDescriptors(proto);
ownProps.forEach(([key, value]) => {
// got overriden
if (key in proto_desc) {
// apply the one from our class
Object.defineProperty(this, key, proto_desc[key]);
// set again the own property (call setters)
this[key] = value;
}
});
}
static get is() {
return 'component-with-async-init'
}
get value() {
console.log("Getting value")
return "VALUE FROM GETTER"
}
set value(v) {
console.log("Setting value")
}
getSomeValue() {
return "SOME VALUE"
}
}
window.customElements.define(ComponentWithAsyncInit.is, ComponentWithAsyncInit);
customElements.upgrade(document.querySelector('component-with-async-init'))
console.log("getSomeValue method:") // note that this will get overidden anyway
console.log(document.querySelector('component-with-async-init').getSomeValue)
console.log("Reading value:")
console.log(document.querySelector('component-with-async-init').value)
<component-with-async-init></component-with-async-init>
但这听起来真的很难看,并且不会在初始化之前阻止诸如方法调用之类的事情。
所以最好在使用这些元素之前等待您的元素注册。
customElements.whenDefined("component-with-async-init")
.then(() => {
document.querySelector('component-with-async-init').value = "SETTERS GETTERS KILLED"
document.querySelector('component-with-async-init').getSomeValue = "GET SOME VALUE KILLED"
console.log("getSomeValue method:") // note that this will get overidden anyway
console.log(document.querySelector('component-with-async-init').getSomeValue)
console.log("Reading value:")
console.log(document.querySelector('component-with-async-init').value)
});
class ComponentWithAsyncInit extends HTMLElement{
static get is() {
return 'component-with-async-init'
}
get value(){
console.log("Getting value")
return "VALUE FROM GETTER"
}
set value(v){
console.log("Setting value")
}
getSomeValue(){
return "SOME VALUE"
}
}
window.customElements.define(ComponentWithAsyncInit.is, ComponentWithAsyncInit);
<component-with-async-init></component-with-async-init>