Shadow DOM、模板与 slot
Shadow DOM 建立组件内部的 DOM 和样式边界,但它不是安全边界。外部不能用普通选择器穿透 shadow root,组件仍需设计可访问名称、焦点和事件。slot 让消费者提供内容,slotchange 只反映分配变化,不是所有后代文本变化。
js
const template = document.createElement('template')
template.innerHTML = `<style>:host{display:block}</style><button><slot>默认</slot></button>`
class ActionCard extends HTMLElement {
constructor() { super(); this.attachShadow({ mode: 'open' }).append(template.content.cloneNode(true)) }
}
customElements.define('action-card', ActionCard)练习
让组件暴露 variant 属性改变样式,并让外部通过默认 slot 提供按钮名称;说明 shadow 内 button 的可访问名称来自哪里。
核心机制与实现
自定义元素把生命周期、Shadow DOM、slot、属性与事件组成公开契约;跨边界时尤其要明确事件是否 composed、表单是否关联。
围绕“Shadow DOM、模板与 slot”先写出输入 → 中间状态 → 可观察结果,再实现最小闭环。不要把事件监听、异步请求、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 课程总览。