constructor 中不能操作 dom 是因为此时元素尚未挂载,this.shadowroot 为 null,document.queryselector 也查不到自身;正确做法仅限声明私有变量、绑定方法和初始化属性,不触发副作用。

constructor 里为什么不能操作 DOM
因为此时元素还没挂载,this.shadowRoot 是 null,document.querySelector 也查不到自己——不是浏览器“没准备好”,而是标准规定它就不可用。
常见错误包括:this.shadowRoot.innerHTML = ''、this.querySelector('button')、甚至在 super() 后加 setTimeout 做异步初始化。这些都会直接报错或静默失败。
✅ 正确做法只做三件事:
- 声明私有变量(如
this._timer = null) - 绑定方法(
this.handleClick = this.handleClick.bind(this)) - 初始化属性(
this.size = 'medium'),但不触发副作用
connectedCallback 是发起请求和渲染的唯一可靠时机
它只在元素真正插入主 DOM 树时触发一次(注意:不是写在 HTML 里就立刻执行,也不是每次 appendChild() 都调用),此时 this.shadowRoot 可用,fetch、addEventListener、requestAnimationFrame 全部安全。
⚠️ 容易忽略的点:
- 该钩子可能被多次调用(比如元素被移出又重新插入),所有逻辑必须可重入;建议用布尔标志位(如
this._isInitialized)防重复请求 - 若组件依赖属性初始值,需在
connectedCallback中显式调用一次this.attributeChangedCallback('size', null, this.getAttribute('size'))同步状态 - 不要在这里做 heavy rendering,避免阻塞主线程;复杂 UI 建议配合
requestIdleCallback
attributeChangedCallback 必须配 observedAttributes 才生效
写了方法但没声明监听列表?等于没写。浏览器根本不会调用它。
正确写法是静态 getter:
static get observedAttributes() {
return ['size', 'theme', 'data-id'];
}
关键细节:
- 属性名严格匹配 HTML 写法:
data-id就是'data-id',不能写成dataId或dataid - 仅响应
setAttribute()或 HTML 解析变更,el.size = 'large'是 property 赋值,不触发回调 -
oldValue在首次设置时为null,别拿它做非空判断来跳过逻辑 - 若需同步 property 和 attribute,得在 setter 里手动调用
this.setAttribute('size', value)
disconnectedCallback 不是“保险锁”,而是“尽力而为”的清理入口
它是你释放定时器、取消 fetch、解绑事件的最后确定机会——但浏览器不保证它一定执行。页面强制刷新、崩溃、标签页关闭时,这个钩子可能完全不运行。
所以:
- 不要在里面做关键数据持久化(比如写
localStorage),应提前保存或双保险设计 - 清理逻辑要幂等:检查
this._timer是否存在再clearTimeout,避免重复调用报错 - 避免访问
document或其他已脱离 DOM 的节点,此时引用可能已失效 - 第三方库实例(如 Chart.js 实例)必须在这里
.destroy(),否则内存泄漏风险极高
最常被忽略的是:自定义元素一旦漏掉 disconnectedCallback,反复挂载/卸载几次后,内存占用就会明显上涨——这问题在线上环境才暴露,调试成本很高。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











