自定义元素命名必须含短横线且全小写,如date-picker;constructor仅初始化并attachshadow,dom操作须移至connectedcallback;表单元素需formassociated+attachinternals+setformvalue才能提交数据。

自定义元素命名必须带短横线且全小写
浏览器注册时直接拒绝非法名称,myinput、MyInput、userProfile 都会抛出 DOMException,根本不会执行后续逻辑。这不是警告,是硬性拦截。
必须满足两个条件:含至少一个 ASCII 短横线(-),且全部字符为小写字母或数字。常见合规名如 date-picker、form-fieldset、status-badge。
- 禁止下划线:
my_button❌ - 禁止大写:
UserCard❌ - 禁止原生标签名:
div、slot、template❌(会覆盖或冲突) - 语义优先但需唯一,避免缩写歧义,比如
usr-crd不如user-card直观
constructor 只做初始化,DOM 操作必须移入 connectedCallback
在 constructor 里调用 this.querySelector、读取 this.getAttribute、设置 innerHTML 或绑定事件,大概率返回 null 或静默失败——因为此时元素还没挂载到 DOM,this.shadowRoot 也是 null。
正确分工非常明确:
-
constructor:只调super()、this.attachShadow({ mode: 'open' })、声明私有字段(如this.count = 0) -
connectedCallback:查 DOM、读属性、监听子元素事件、调用this.internals.setFormValue()、渲染模板克隆 - 首次属性值不会触发
attributeChangedCallback,必须在connectedCallback中手动读取this.getAttribute('value')
让自定义表单元素真正提交数据,绕不开 formAssociated
写了 <my-input></my-input> 却收不到值?不是样式没生效,是压根没接入表单生命周期。90% 的表单类自定义元素卡在这一步。
三件事缺一不可:
- 定义时显式传入
{ formAssociated: true },否则this.attachInternals()会报错 -
constructor中调用this.attachInternals()获取this.internals -
connectedCallback中监听子<input>的input或change事件,并调用this.internals.setFormValue(value)
别试图继承 HTMLInputElement——Chrome 100+ 已限制,Firefox/Safari 基本不支持。现实路径只有自治型(autonomous)自定义元素 + attachInternals()。
Shadow DOM 样式必须内联或 adopt,不能靠外部 CSS 文件
把 <link rel="stylesheet" href="style.css"> 放进自定义元素内部,或者在 connectedCallback 里动态插入 <link>,样式基本不生效。Shadow DOM 的边界天然隔离外部资源加载逻辑。
可靠做法只有一种:样式字符串必须注入到 shadowRoot 内部。
- 最简方式:
this.shadowRoot.innerHTML = '<style>...</style>' + templateHTML - 更优方式:预编译样式为常量字符串,或用模板函数生成,避免重复解析
- 现代方案(Chrome 120+/Safari 17.4+):用
adoptedStyleSheets,但需 fallback 到内联方案 - 禁止在 light DOM 写组件样式,否则会污染全局;也禁止在
<template></template>里放<style></style>标签再克隆——它不会随 fragment 一起被注入 shadowRoot
复杂组件的样式边界和表单参与能力,是真正区分“能跑”和“可用”的分水岭。这两个点踩错,调试成本远高于重写逻辑。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











