data-* 属性是严格规范的 dom 原生机制:命名须全小写、连字符分隔(如 data-user-id),读取用 dataset.userid,写入必须 setattribute;值恒为字符串,json/布尔/数字需手动解析转换;禁止传敏感信息,ssr 中优先用 getattribute 安全读取。

data-* 属性不是“随便加个属性就能传参”的快捷方式,它是一套有严格命名、读写规则和类型约束的 DOM 原生机制——写错名就读不到,混用 API 就值不一致,不手动转类型就逻辑翻车。
data-* 属性名为什么写了还是读不到
浏览器只解析符合规范的 data- 属性:必须全小写,连字符分隔,不能含大写字母、下划线、数字开头(如 data-userId、data_user_id、data-2024-id 全部无效)。只有 data-user-id 这类才被识别,并映射为 dataset.userId。
-
data-api-url→ 可读为dataset.apiUrl -
data-json-config→ 可读为dataset.jsonConfig -
data-2024-report→ 不能用点号访问,必须写dataset["2024Report"] - 写成
data-UID或data-User-Id,DevTools 里都看不到,dataset.uid或dataset.userId永远是undefined
dataset 读写必须和 setAttribute 配合使用
dataset 是只读映射,不是双向绑定。直接赋值 el.dataset.foo = "bar" 不会更新 DOM 属性,下次刷新或服务端渲染后就丢失;而 setAttribute("data-foo", "baz") 才真正写入 DOM。
- 读取优先用
el.dataset.xxx(语义清晰、自动驼峰) - 写入必须用
el.setAttribute("data-xxx", "value")(否则不持久) - 判断是否存在:先
"xxx" in el.dataset,再取值,避免undefined报错 - 删除必须用
el.removeAttribute("data-xxx"),delete el.dataset.xxx无效
JSON 和布尔/数字值必须手动解析和转换
dataset 返回的永远是字符串。哪怕 HTML 里写 data-active="true" 或 data-config='{"timeout":5000}',JS 里拿到的也只是 "true" 和 '{"timeout":5000}' —— 不解析、不转类型、不兜底。
- 布尔判断别用
Boolean(el.dataset.active),改用el.dataset.active === "true" - 数字转换用
+el.dataset.count或Number(el.dataset.count),不用parseInt()(防截断) - JSON 必须
try { JSON.parse(el.dataset.config || "{}") } catch(e) { ... } - 服务端输出 JSON 时,务必先
JSON.stringify(),再做 HTML 实体转义(如双引号变")
敏感信息和框架场景下的典型陷阱
data-* 是明文挂载点,不是安全通道。所有值在源码、DevTools、网络响应中全部可见。Vue/React 也不会自动透传这些属性到子组件根节点——你看到的“消失”,是框架设计使然,不是 bug。
- 绝对禁止传
data-token、data-api-key、data-phone等敏感字段 - SSR 场景下,首次 JS 执行前
dataset可能为空,建议用getAttribute()安全读取 - Vue 中需设
inheritAttrs: false并手动v-bind="$attrs";React 中需解构props["data-track-id"] - 单页应用中,如果初始配置来自服务端,
是最稳的挂载位置,比<div id="app"> 更早可用<p>最容易被忽略的是:dataset 的驼峰转换只认连字符后字母,不处理数字或下划线;写入必须走 <code>setAttribute;JSON 解析失败不加try/catch会直接中断脚本。这些不是“可选优化”,而是不踩就出问题的硬边界。











