customElements.define 必须传继承 HTMLElement 的类,类名含短横线,重复注册需守卫;attributeChangedCallback 仅响应 setAttribute,需声明 observedAttributes;Shadow DOM 样式极简,仅限基础属性,图标退化为文字,语义完整性不可妥协。

customElements.define 必须传 class,不能传对象或箭头函数
注册失败最常见的原因是把 customElements.define 的第二个参数写成普通对象或箭头函数。浏览器只接受显式继承 HTMLElement 的类。
- ❌ 错误写法:
customElements.define('x-button', { connectedCallback() {} }) - ✅ 正确写法:
class XButton extends HTMLElement { connectedCallback() { this.innerHTML = '<button><slot></slot></button>'; } },再调用customElements.define('x-button', XButton) - 类名必须含短横线(如
x-button),buttonx会直接抛DOMException: The element name must contain a hyphen - 重复注册会报错,开发时建议加守卫:
if (!customElements.get('x-button')) customElements.define('x-button', XButton);
attributeChangedCallback 只响应 setAttribute,不响应 JS 属性赋值
这个回调不是“属性变更监听器”,它只对 HTML 属性变更(包括初始渲染时的属性)生效,对 el.disabled = true 这类 JS 属性操作完全无感。
- 必须在
static get observedAttributes()中显式声明要监听的属性,漏写就收不到回调 -
oldValue在首次触发时是null,别直接用===判断相等;newValue总是字符串,布尔属性(如disabled)需手动转布尔:newValue !== null - 想同步 JS 属性和 HTML 属性,得自己补逻辑:在 setter 里调用
this.setAttribute('disabled', '')或this.removeAttribute('disabled')
Shadow DOM 内部样式必须极简,否则“封装”就失效了
Shadow DOM 提供样式隔离,但一旦在里面塞 box-shadow、border、background-color 或 Web Font,就违背了极简设计系统的初衷——它不是为了“看起来更精致”,而是为了语义清晰、可索引、无障碍友好。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 允许的 CSS 属性仅限:
font-family(系统字体链)、line-height(1.5 或 1.6)、margin-block、color(#000 / #333 / #666)、font-weight(400 / 600) - 图标必须退化为文字:
[PDF]替代下载图标,→替代右箭头,禁用 inline SVG 和 icon font - 所有间距靠
margin-block控制,不用空 div 或额外 class - 外部无法穿透 Shadow DOM 修改样式,除非你主动暴露
CSS custom property,比如--ds-button-bg
自定义元素不是“套壳”,它天然承载语义与约束
写 <ds-button disabled> 和写 <button class="ds-button" disabled> 是两回事:前者是真实 HTMLElement 实例,Lighthouse 能校验它的 role,axe 工具能读取它的 aria 属性,搜索引擎能识别它的语义层级;后者只是个带 class 的 div,所有语义都靠人工维护,极易脱节。
立即学习“前端免费学习笔记(深入)”;
-
ds-heading就是标题,不是“看起来像标题的 div”;ds-form就是表单容器,role="form" 必须由组件自身保证 - 样式穿透风险归零:外部
body * { font-size: 12px }不会影响ds-button内部字体大小 - 事件冒泡路径清晰,
click从 button 冒泡到ds-button再到 document,中间无框架层干扰
真正难的不是写一个能跑的自定义元素,而是坚持不加装饰性属性、不妥协语义完整性、不在 Shadow DOM 里偷偷塞 box-shadow —— 封装一旦破功,就只剩下一个更重的 class。


















