data-* 属性仅适合存静态、轻量、非敏感的视图元数据,不可替代 react state 或存敏感信息;读写须用 dataset 或 setattribute/getattribute,值恒为字符串,特殊字符需引号,ssr 后易状态不一致。

data-* 属性不能当状态用,它只适合存静态、轻量、非敏感的视图层元数据——用错场景(比如当 React state、存 token、解析 JSON)会立刻导致类型错判、DOM 同步脱节或安全泄露。
data-* 读写必须用 dataset 或 getAttribute/setAttribute
HTML 中写 data-user-id="1024",JS 里不能直接访问 el.dataset.userid 或 el.dataset.UserId;正确写法是 el.dataset.userId(连字符自动转驼峰,首字母小写)。如果属性名含数字开头,如 data-1st-place,就得用 el.dataset["1st-place"],否则语法报错。
写入时别用 el.dataset.active = "false"——这不会触发 DOM 属性变更,也不会被 attributeChangedCallback 捕获。必须用 el.setAttribute("data-active", "false") 才真正更新属性。
- 读取值永远是字符串:
data-count="42"→el.dataset.count === "42",需手动转类型:Number(el.dataset.count) - CSS 选择器匹配含特殊字符的值必须加引号:
[data-status="pending:review"],不加会失效 - 服务端渲染(SSR)后客户端 JS 修改
data-值,框架若没监听属性变更,首屏与 JS 状态就会不一致
哪些场景真能用 data-* 存“状态”
仅限:状态只服务于当前元素或其直系子元素、不依赖异步、无需跨组件同步、可由 DOM 直接推导。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 折叠面板是否展开:
<div class="panel" data-expanded="true">,配合 CSS <code>[data-expanded="true"] { max-height: 500px; } - 表格行编辑态:
<tr data-editing="true">,JS 切换 <code>td内容为input,不涉及其他行逻辑 - Tab 导航激活项:
<button data-active="true">首页</button>,CSS 控制高亮,无状态广播需求 -
id查询是 O(1),class匹配依赖选择器引擎,深层嵌套下有性能感知 -
data-config='{"theme":"dark"}'必须手动JSON.parse(),且要try/catch,否则组件初始化直接中断 - 多个实例共用同一
data-值但没隔离作用域,比如批量渲染卡片却都写data-index="0",会导致逻辑错乱 - 在
constructor()里就读this.dataset.userId?错——此时shadowRoot还未挂载,拿不到值 - 改了
this.dataset.active,不会进attributeChangedCallback;必须调用this.setAttribute('active', 'false') -
MutationObserver能监听任意节点的data-变更,attributeChangedCallback只对已注册的自定义元素生效
一旦出现「多个组件响应同一变化」或「需持久化到 localStorage」,就必须交给 JS 变量、useState 或 store 管理,而不是往 DOM 上堆 data-。
data-* 和 hidden/class 的行为差异必须分清
hidden 是布尔属性,存在即隐藏,删除即显示,但旧版 Safari/IE 不稳定,得 fallback 到 style.display = "none";class 改变会触发重绘,且 element.classList.toggle() 安全可靠;而 data- 变更完全不触发任何浏览器行为——不重绘、不重排、不通知 JS。
自定义元素中想让 data-* “参与状态追踪”,必须走 attributeChangedCallback
原生 data- 没响应式能力,但配合 customElements 可桥接。关键点:在 static get observedAttributes() 里声明属性名(不含 data- 前缀),例如 ['user-id', 'active'],不是 ['data-user-id']。
真正容易被忽略的是:所有 data- 值都是字符串,没有布尔或数字语义——data-active="true" 在 JS 里是字符串 "true",if (el.dataset.active) 恒为真,必须显式判断 === "true"。










