Custom Element 生命周期回调由浏览器自动触发,非HTML实现;connectedCallback仅在首次插入主DOM树时执行;attributeChangedCallback需配合observedAttributes且监听HTML属性;disconnectedCallback不可靠,需兜底清理;constructor中禁止DOM操作,仅限轻量初始化。

Custom Element 的生命周期回调不是“HTML 做的”,而是浏览器在 DOM 变化时自动触发的 JS 类方法;副作用管理必须依赖这些回调时机,而非手动轮询或 DOM 就绪后硬塞逻辑。
connectedCallback 为什么只在首次插入时执行
它不是“元素一存在就调用”,而是严格绑定到「被插入 active document 的主 DOM 树」这一动作。静态写在 HTML 中的 <my-card></my-card>,会在 parser 解析完该标签、完成实例化并挂载进 document.body(或任意可渲染父节点)后触发;动态创建则必须显式调用 appendChild() 才算“连接”。
- 重复
appendChild()/removeChild()不会再次触发 —— 它只认“首次连接” - 在 Shadow DOM 内部插入该自定义元素,不会触发父文档的
connectedCallback - 若元素被
document.adoptNode()移入新文档,也不会触发此回调(那是adoptedCallback的职责)
attributeChangedCallback 必须配合 observedAttributes 才生效
这个回调默认完全静默,哪怕你写了方法体,只要没在类上声明 static get observedAttributes(),属性变再多遍也不会调用。它监听的是 attribute(HTML 属性),不是 property(JS 属性),所以 this.disabled = true 不触发,但 el.setAttribute('disabled', '') 会。
-
observedAttributes必须返回字符串数组,如['size', 'data-id', 'aria-label'] - 属性名大小写敏感,
data-id和DATA-ID是两个不同监听项 - 首次设置属性时,
oldValue参数为null,后续变更才带旧值 - 不支持通配符或正则,无法监听所有以
data-开头的属性
disconnectedCallback 不是可靠的清理入口
它是唯一明确设计用于资源释放的回调,但浏览器不保证它总被执行 —— 页面刷新、崩溃、标签页关闭时,JS 上下文可能直接销毁,disconnectedCallback 就被跳过了。因此不能把关键清理逻辑(如 WebSocket 关闭、全局事件监听器移除)只放在这里。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
立即学习“前端免费学习笔记(深入)”;
- 推荐搭配
beforeunload或pagehide做兜底,尤其涉及跨页面状态时 - 定时器、
IntersectionObserver、ResizeObserver等必须在disconnectedCallback中显式.disconnect()或clearTimeout() - 如果组件内持有了外部引用(如传入的 callback 函数),需主动置
null防止内存泄漏
constructor 里能做什么、不能做什么
这是整个生命周期中唯一同步、必然执行的阶段,但它极其受限:DOM 还不存在,this 还没挂载到任何树上,连 document 都可能未就绪(比如在 <head> 中定义类)。它只适合做最轻量的初始化。
- 必须第一行调用
super(),否则报错 - 禁止访问
this.innerHTML、this.shadowRoot、document.querySelector等任何 DOM 相关 API - 可以初始化私有字段(
this._cache = new Map())、绑定方法(this.handleClick = this.handleClick.bind(this)) - 不能调用
customElements.define()自身,会导致循环定义错误
真正容易被忽略的是:这些回调之间没有执行顺序保障 —— 比如 connectedCallback 开始执行时,attributeChangedCallback 可能已在队列中等待,也可能还没排队。不要假设它们串行发生,所有状态更新都得考虑竞态。


















