data-*属性只能存字符串,JSON数据必须先用JSON.stringify()序列化再赋值,读取时需用JSON.parse()解析;键名自动转驼峰命名;仅适合小量静态初始化数据,大JSON应改用script标签或API加载。

data-* 属性只能存字符串,JSON 必须先序列化
HTML 的 data-* 属性不支持直接存对象或数组,浏览器会把非字符串值自动转成字符串(比如 [object Object]),所以必须手动 JSON.stringify()。否则取出来就是个空对象或 [object Object],根本不是你想要的 JSON 数据。
常见错误是直接赋值:el.dataset.config = { theme: 'dark' } —— 这样存进去的其实是 "[object Object]",后续 JSON.parse(el.dataset.config) 会报 SyntaxError: Unexpected token o。
- 必须用
JSON.stringify()转成字符串再赋值:el.dataset.config = JSON.stringify({ theme: 'dark', timeout: 3000 }) - 读取时必须显式
JSON.parse():const config = JSON.parse(el.dataset.config) - 如果 JSON 内容含双引号或换行,
JSON.stringify()已自动处理,无需额外转义
dataset 键名会自动转为 camelCase,注意命名映射
data-user-info 对应的 JS 访问属性是 dataset.userInfo,不是 dataset.user-info 或 dataset["user-info"]。连字符会被移除,后续单词首字母大写。这个转换是只读映射,不能反向操作。
所以如果你存的是 data-api-endpoint,取的时候必须写 el.dataset.apiEndpoint;若误写成 el.dataset["api-endpoint"],返回的是 undefined。
立即学习“前端免费学习笔记(深入)”;
- 推荐命名时用短横线分隔(
data-app-config),JS 侧统一用驼峰(dataset.appConfig) - 避免用
data-json这类泛化名,易与其它 data 属性冲突;优先语义化,如data-initial-state - 不要依赖
dataset的动态响应性:修改dataset.xxx不会触发 DOM 属性同步更新(反之亦然)
大 JSON 或频繁读写时,性能和可维护性明显下降
把几百 KB 的 JSON 塞进 data- 属性,会导致 HTML 体积膨胀、解析变慢,且 DevTools 查看困难。更严重的是,每次读取都要 JSON.parse(),重复调用开销不可忽视。
典型误用场景:把整个 Redux state 或表格列配置全塞进一个 data-grid-config。页面渲染后还要反复 parse,既拖慢首屏,又增加出错概率(比如某次忘记 parse 就直接用)。
- 仅适合小量、静态、一次性初始化数据(如按钮默认行为参数、组件初始 mode)
- 超过 2KB 的 JSON,建议改用
<script type="application/json">块,或通过 API 异步加载 - 若需多次读取同一份数据,解析后应缓存到变量里,避免重复
JSON.parse()
服务端渲染时要注意引号闭合和 XSS 风险
后端模板(如 Django、Jinja、PHP)拼接 JSON 到 data- 属性时,若没正确转义,极易导致 HTML 结构破坏或 XSS。例如:data-config="{{ user_input }}" 中 user_input 含 " 或 <script>,会提前闭合属性或执行脚本。
安全做法不是靠 JS 端补救,而是在服务端生成时就确保字符串合法:
- 用语言原生 JSON encoder(如 Python 的
json.dumps()+ HTML-escape)输出到属性值 - 避免在模板中手动拼接:
data-config='{"name": "{{ name }}"}'是危险的 - 若必须前端注入,用
Element.setAttribute('data-xxx', JSON.stringify(obj)),它会自动处理引号转义
实际用起来最稳的方式就三条:字符串化再存、驼峰读、小数据才用。复杂状态别硬塞 data 属性里——它不是 localStorage 替代品,只是轻量初始化通道。



















