constructor只能调用super()、初始化私有字段、绑定方法;禁止任何dom操作或异步请求,因无dom上下文。

constructor 里只能干啥,不能干啥
constructor 是类实例化时最先执行的钩子,但它**完全没 DOM 上下文**——this.shadowRoot 是 null,document 查不到自己,任何 DOM 操作都会立刻报 TypeError。
✅ 只能做三件事:
- 调用 super()(必须第一行)
- 初始化私有字段,比如 this._timer = null、this._data = null
- 绑定方法: this.handleClick = this.handleClick.bind(this)
❌ 禁止:
- 写 this.shadowRoot.innerHTML = '...'
- 调用 fetch() 或 this.querySelector()
- 访问 document.body 或其他外部节点
connectedCallback 才是真正“初始化”的地方
这个钩子只在元素**首次插入 document 主树**时触发,此时 this.shadowRoot 已就位,DOM 完全可用,是发起请求、渲染 UI、绑定事件的唯一可靠时机。
常见误判:
- HTML 里写了 <my-card></my-card>,但 customElements.define() 还没执行 → 永远不触发
- innerHTML = '<my-card></my-card>' 插入 → 不触发,浏览器走字符串解析路径,绕过生命周期
- document.createElement('my-card') 后没调 parent.appendChild(el) → 不挂载,不触发
实操建议:
- 在 connectedCallback 开头加守卫: if (this._initialized) return; this._initialized = true;
- 首次读取初始属性: const id = this.getAttribute('data-id');
- 主动同步一次属性变更: this.attributeChangedCallback('data-id', null, id);(如果需要)
attributeChangedCallback 为什么没反应?检查 observedAttributes
这个回调不会自动监听任意属性,必须显式声明要观察的属性名,且大小写、连字符必须和 HTML 中完全一致,否则改了也静默失效。
错误示例:
- HTML 写 <my-input data-user-id="42"></my-input>,但 observedAttributes 返回 ['userId'] → 不触发
- 写成 static observedAttributes = ['data-user-id'](漏掉 get)→ 无效
- 直接赋值 el.value = 'abc'(property 操作)→ 不触发;必须用 el.setAttribute('value', 'abc')
正确写法:static get observedAttributes() { return ['data-user-id', 'disabled']; }
注意:
- 首次渲染的属性值不会进这个回调,oldValue 是 null
- 初始化逻辑仍得放在 connectedCallback 里兜底
disconnectedCallback 清理不保险,别依赖它存关键数据
它只在元素被显式移除时大概率触发,但页面刷新、关闭 tab、后台销毁等场景下,浏览器可能直接释放上下文,跳过此回调。
所以:
- 所有定时器必须在这里 clearTimeout(this._timer),但也要加 fallback(比如用 AbortController 控制 fetch)
- 不要在里面写 localStorage.setItem() 这类关键持久化操作
- 清理逻辑要幂等:加 guard 判断,比如 if (this._timer) { clearTimeout(this._timer); this._timer = null; }
- 避免访问已脱离 DOM 的节点,this.parentElement 可能为 null
真正容易被忽略的是:你写的清理代码,可能从来就没被执行过。别把它当“必达终点”,而是当作“尽力而为的最后机会”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











