html自定义元素不提供自动逻辑抽取,仅注册标签并响应生命周期;所有dom操作、事件监听、第三方库集成等必须手动在connectedcallback中实现,并在disconnectedcallback中清理资源。

HTML自定义元素本身不提供“逻辑抽取”这个功能,它只负责注册标签、响应生命周期和属性变更;所谓“逻辑抽取”,其实是开发者把重复交互、状态管理、第三方集成等代码从页面中拎出来,封装进自定义元素类里——本质是手动组织和复用 JS 逻辑,不是框架自动做的。
customElements.define() 只注册,不执行任何逻辑
调用 customElements.define('my-button', MyButton) 后,浏览器只是记住了这个标签名,并不会自动初始化实例、绑定事件或渲染内容。如果你没在 connectedCallback() 里写 this.shadowRoot.querySelector('button').addEventListener(),那点击就完全没反应。
- 常见错误:在
constructor()里操作this.shadowRoot—— 此时 DOM 还没挂载,querySelector返回null - 正确时机:所有 DOM 操作、事件监听、第三方库初始化,都必须放在
connectedCallback()中 - 如果组件可能被多次挂载/卸载(比如路由切换),记得在
disconnectedCallback()里清理定时器、IntersectionObserver、EventSource等资源
attributeChangedCallback 不响应 JS 属性赋值
attributeChangedCallback 只对 HTML 属性变更敏感,比如 el.setAttribute('disabled', 'true') 或初始写在 HTML 里的 <my-input disabled></my-input>。但 el.disabled = true 这种 JS 属性赋值,它根本收不到。
- 想让 JS 属性和 UI 同步?得自己写 setter:
set disabled(v) { this.setAttribute('disabled', v ? '' : null); } - 必须在
static get observedAttributes()里显式列出要监听的属性名,漏一个就失效 -
oldValue在首次初始化时为null,别直接=== 'true'判断;布尔属性的newValue是字符串"true"或null,需手动转布尔
封装第三方库时,DOM 和实例生命周期必须对齐
用自定义元素包 Chart.js 或 Leaflet,最常崩在“时机错位”:DOM 节点已挂载,但 window.Chart 还没加载完;或者组件卸载了,图表实例还在后台跑着定时器。
- 注册前必须确认库已就绪:可用
import('chart.js').then(() => customElements.define(...)),或轮询window.Chart - 首次渲染用
new Chart(ctx, config),后续更新必须调this._chart.data = ...; this._chart.update(),不能只改 HTML 属性 -
disconnectedCallback里必须显式调this._chart.destroy()或this._map.remove(),否则内存泄漏 - Shadow DOM 会隔离样式和节点查询,第三方库通常需要 light DOM 容器或显式传入
document.getElementById()获取的节点
真正容易被忽略的是:自定义元素不是魔法盒,它把“谁来触发”“谁来清理”“谁来同步”这些责任全交还给你。哪怕只封装一个带 loading 状态的按钮,也得自己管好 loading 属性到 class 的映射、到内部 button.disabled 的同步、再到 Promise 完成后的还原逻辑——没有一处是自动的。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











