Skip to content

自定义元素与生命周期 ​

自定义元素的价值是给浏览器一个稳定的 HTML 标签和生命周期,不是把任意组件代码塞进 DOM。标签名必须包含连字符,定义前后的元素行为不同;connectedCallback 适合建立监听,disconnectedCallback 必须清理自己拥有的资源。

js
class StatusBadge extends HTMLElement {
  connectedCallback() { this.render() }
  static get observedAttributes() { return ['status'] }
  attributeChangedCallback() { this.render() }
  render() { this.textContent = this.getAttribute('status') ?? 'unknown' }
}
customElements.define('status-badge', StatusBadge)

不要在构造函数里读取尚未升级完成的属性或操作子节点;把输入变化集中到 attributeChangedCallback,把副作用和清理放在成对生命周期。

练习 ​

添加 interval 属性,让徽章每秒更新一次时间;元素从 DOM 删除后计时器必须停止。

核心机制与实现 ​

自定义元素把生命周期、Shadow DOM、slot、属性与事件组成公开契约;跨边界时尤其要明确事件是否 composed、表单是否关联。

围绕“自定义元素与生命周期”先写出输入 → 中间状态 → 可观察结果,再实现最小闭环。不要把事件监听、异步请求、DOM 读取或 store 写入混在一个函数里;每个副作用都要有创建、完成、取消和清理路径。

js
class QuantityInput extends HTMLElement {
  static formAssociated = true
  static observedAttributes = ['value']
  #internals = this.attachInternals()
  attributeChangedCallback() { this.#internals.setFormValue(this.getAttribute('value') ?? '') }
}
customElements.define('quantity-input', QuantityInput)

逐步运行:先只验证成功路径,再改变一个输入;记录时间顺序、对象身份、错误类型和最终可见状态。若环境不支持某个浏览器 API,要把静态协议验证和真实浏览器验证分开记录。

失败边界与变体 ​

  • 输入为空、重复、过期或非法时,不应静默覆盖有效状态。
  • 组件卸载、路由离开、标签页关闭或请求取消时,资源必须停止写入。
  • 跨文档、Shadow DOM、Worker、SSR 或多标签页会改变可见边界,不能沿用单页面直觉。
  • 需要持久化时记录 schema 版本和迁移失败策略;需要全局状态时先证明局部状态不足。

可运行实验与练习 ​

在真实浏览器测试定义前后升级、shadow 事件、焦点、表单提交和元素移除清理;Node 模拟只能验证纯逻辑,不能代替这些平台行为。

练习要求:实现一个最小版本,补一个失败测试,再给出一个替代方案及其适用条件。答案应包括实际输入序列、观察结果、修复原因和未覆盖的浏览器差异。

查看本节答案

下一步 ​

完成练习后打开本章答案,再回到Web Components 课程总览。

用心记录每一步成长