Web Components中自定义元素setter在组件内为何被覆盖而非触发?
核心原因:自定义元素的升级时机问题
当你在serial-log-setter-broken的构造函数中直接创建serial-log-entry并设置line_number时,该元素还处于未升级状态——它只是一个普通的HTMLElement实例,还没有被浏览器关联到你定义的SerialLogEntry自定义类。
此时直接赋值.line_number,只是给这个普通元素添加了一个普通的实例属性,完全不会触发你在自定义类中定义的setter。更糟的是,当后续元素被添加到DOM完成升级后,实例上已存在的line_number普通属性会覆盖原型链上的setter/getter,导致后续任何对.line_number的赋值都无法触发你定义的逻辑,看起来就像setter被删除了一样。
而在组件外部或先添加元素到DOM再设置属性时,元素已经完成了自定义升级,此时赋值.line_number会正常触发自定义类的setter,自然可以正常工作。
解决方法
手动升级元素后再设置属性
在构造函数中创建元素后,调用customElements.upgrade()强制完成升级,再赋值属性:const entry = document.createElement('serial-log-entry'); customElements.upgrade(entry); entry.line_number = 1;先添加到DOM再设置属性
也就是你serial-log-setter-working采用的方式,利用DOM添加时自动触发升级的特性,确保元素升级后再操作属性。使用
setAttribute()替代直接赋值
如果你的自定义类定义了observedAttributes并实现了attributeChangedCallback,可以通过设置HTML属性来触发逻辑,即使元素未升级,后续升级时也会同步处理:const entry = document.createElement('serial-log-entry'); entry.setAttribute('line_number', '1');
内容的提问来源于stack exchange,提问作者Densaugeo

