自定义元素生命周期钩子是强制接口:constructor仅初始化,不可操作DOM;connectedCallback是发起请求和初始化UI的唯一可靠时机;attributeChangedCallback需声明observedAttributes才生效;disconnectedCallback必须清理资源防内存泄漏。
HTML 自定义元素(Custom Elements)的生命周期钩子不是“可选增强”,而是资源管理的强制接口——漏掉 disconnectedCallback 很可能造成内存泄漏,而过早在 constructor 里操作 DOM 会直接报错。
这是最容易踩的坑:自定义元素类的 constructor 在元素被创建但尚未插入 DOM 时就执行,此时 this.shadowRoot 为 null,document.querySelector 也查不到自己。
this._timer = null)this.shadowRoot.innerHTML = '...' 或 this.querySelector('input')
constructor 中调用 super() 之后再写异步逻辑(如 setTimeout),因为此时元素仍处于未挂载状态,DOM 不可用connectedCallback 触发表示元素已插入文档,此时 this.shadowRoot 可用,且能安全调用 fetch、addEventListener、requestAnimationFrame 等。
connectedCallback 中检查是否已初始化(用布尔标志位),避免重复挂载时重复请求或重复绑定connectedCallback 中显式调用一次 this.attributeChangedCallback 同步初始值即使你写了 attributeChangedCallback 方法,如果没在类上定义静态 getter observedAttributes,浏览器根本不会调用它。
立即学习“前端免费学习笔记(深入)”;
static get observedAttributes() { return ['label', 'disabled', 'value']; }
attributeChangedCallback(attrName, oldValue, newValue),其中 oldValue 和 newValue 均为字符串,需手动转换类型(如 JSON.parse 或 Number())<my-input value="42">)时,oldValue 为 null,不是 undefined;后续变更才走正常对比这个钩子常被忽略,但它是防止内存泄漏的关键防线。一旦元素从 DOM 移除,但还持有对全局对象的引用(比如 setInterval、window.addEventListener、new MutationObserver),就会导致节点无法被 GC 回收。
clearInterval(this._timer)、removeEventListener、observer.disconnect()
if (this._timer) { clearInterval(this._timer); this._timer = null; }
disconnectedCallback 不保证一定会执行(比如页面刷新、iframe 卸载),所以关键资源释放逻辑不应只依赖它;但组件内可控制的部分(如本组件创建的定时器、观察者)必须在这里清理真正难的不是记住这四个钩子的名字,而是判断哪段逻辑该放在哪个钩子里——尤其是当组件支持 SSR 或跨 shadow boundary 渲染时,connectedCallback 的触发时机可能比预期晚,而 attributeChangedCallback 又不处理初始属性。这时候,一个带状态缓存的初始化守卫比硬编码更可靠。