html本身没有生命周期钩子,纯html是静态标记语言;实现类似mounted/unmounted的逻辑必须通过javascript定义custom elements,借助constructor、connectedcallback、disconnectedcallback等浏览器原生回调。

HTML 本身没有生命周期钩子
直接说结论:纯 HTML 不提供任何生命周期钩子(如 connectedCallback、disconnectedCallback),它只是静态标记语言。想实现类似 React 的 useEffect 或 Vue 的 mounted,必须借助 JavaScript —— 具体来说,是通过 自定义元素(Custom Elements) 的标准 API。
用 customElements.define() 注册时声明钩子
真正起作用的是浏览器原生支持的自定义元素生命周期回调,它们只在继承 HTMLElement 的类中生效:
class MyWidget extends HTMLElement {
constructor() {
super(); // 必须调用
}
connectedCallback() {
// 元素被插入 DOM 时触发(类似 mounted)
console.log('attached');
}
disconnectedCallback() {
// 元素从 DOM 中移除时触发(类似 unmounted)
console.log('detached');
}
attributeChangedCallback(name, oldValue, newValue) {
// 监听特定 attribute 变化(需配合 static observedAttributes)
}
adoptedCallback() {
// 元素被移动到新 document 时触发(极少见)
}
}
customElements.define('my-widget', MyWidget);
-
connectedCallback不保证子节点已渲染,DOM 操作可安全执行,但若依赖子组件,需加requestAnimationFrame或queueMicrotask -
disconnectedCallback是清理定时器、事件监听器的唯一可靠时机;不在此处清理,容易内存泄漏 -
attributeChangedCallback仅对在static observedAttributes中声明的属性生效,且只响应字符串值变化(布尔属性要手动转换)
为什么不能在普通 <div> 上加钩子
<p>常见误解是给任意 HTML 标签加 JS 监听来模拟生命周期,比如监听 <code>DOMNodeInserted —— 这些老式 MutationObserver 方案既不可靠,也无法精准对应“组件实例”的创建/销毁语义:
-
MutationObserver 监听的是 DOM 变动,不是组件实例生命周期;同一元素可能被反复增删,但实例未重建
- 无法区分是首次挂载还是重渲染,也拿不到组件内部状态
- 性能差:需手动过滤目标节点,且无法与 Shadow DOM 隔离
- 现代框架(Lit、Stencil)底层仍基于 Custom Elements 钩子,而非 MutationObserver
兼容性与实际落地注意点
MutationObserver 监听的是 DOM 变动,不是组件实例生命周期;同一元素可能被反复增删,但实例未重建Chrome、Firefox、Safari(16.4+)、Edge 全支持,但 IE 完全不支持;若需兼容旧环境,必须用 polyfill(如 @webcomponents/custom-elements):
- 钩子函数内禁止调用
this.innerHTML = ...或this.appendChild(...),否则可能触发重复connectedCallback(尤其在 SSR hydration 场景下) - 不要在
constructor里访问this.attributes或调用this.shadowRoot—— 此时 DOM 尚未初始化 - 服务端渲染(SSR)后 hydrate 时,
connectedCallback会再次触发,需用 flag 避免重复初始化
真正要扩展生命周期,不是“给 HTML 加钩子”,而是把 HTML 当作模板,用 Custom Elements 构建可复用、有状态、可感知 DOM 存在周期的实体——其余都是绕路。











