d3.js的data()仅建立数据与dom的映射关系,不自动更新界面;必须显式调用enter()创建新元素、update()(在原始selection上)更新属性、exit().remove()清除冗余元素,三者缺一不可。

直接说结论:D3.js 的 select + data() 不是“绑定后自动更新”,而是建立一次映射关系,后续必须显式调用 enter()、update(隐式在 selection 上)、exit().remove() 才能完成 DOM 同步。漏掉任一环节,数据变了但页面不动,或残留旧元素。
为什么 select + data() 之后 DOM 没反应?
因为 data() 只做“连接”(join),不触发任何 DOM 操作。它把数据数组和当前选中的元素按顺序或 key 匹配,生成三个逻辑子集:enter(多出的数据)、update(已有元素对应的数据)、exit(多余的老元素)。但这些子集默认不执行任何动作。
- 常见错误现象:
d3.select("#chart").selectAll("circle").data([10,20,30]);执行完页面一个圆都不画 - 真正起作用的是后续链式调用,比如
.enter().append("circle") - 如果之前已有 5 个
circle,但新数据只有 2 个,默认不会删掉多余的 3 个 —— 必须写.exit().remove()
如何正确写出 enter/update/exit 三段式更新?
这是 D3 数据驱动更新的最小完整单元。省略任意一段,都会导致状态错乱。
-
enter():只对新增数据生效,必须用append()创建新元素,再设属性或样式 -
update:对所有当前已存在且有对应数据的元素生效,直接在原始 selection 上链式调用即可(如.attr("r", d => d)) -
exit().remove():必须显式调用,否则旧元素残留;也可用.exit().transition().remove()做淡出动画
示例(更新一组圆):
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
const circles = svg.selectAll("circle")
.data(data, d => d.id); // 推荐加 key 函数,避免顺序依赖
circles.enter()
.append("circle")
.attr("r", d => d.value)
.merge(circles) // 把 enter 和 update 合并,统一设置
.attr("cx", d => xScale(d.x))
.attr("cy", d => yScale(d.y));
circles.exit().remove();
key 函数为什么不能省?
不传 key 函数时,D3 默认按索引匹配(index-based join)。一旦数据顺序变动、增删中间项,DOM 元素就会错位重用 —— 比如第 2 个数据被删,原第 3 个元素会突然显示第 2 个数据的值,造成视觉跳变。
- 正确做法:给每个数据对象加唯一标识字段(如
id、name),并在data()中传入 key 函数:.data(data, d => d.id) - key 函数返回字符串,D3 用它做哈希比对,确保相同 key 的数据始终绑定到同一个 DOM 元素
- 常见坑:
d => JSON.stringify(d)看似通用,但性能差且可能因浮点精度或属性顺序不同导致 key 不一致
用 .join() 能简化吗?它和三段式什么关系?
.join() 是 D3 v6+ 引入的语法糖,本质就是自动帮你写了 enter/update/exit 三段逻辑,但行为完全可控。它不是黑盒,而是明确封装了 merge 流程。
- 写法更紧凑:
.data(data).join("circle")会自动处理 enter(append)、update(复用)、exit(remove) - 但若要自定义 enter 或 exit 行为(比如加过渡、换标签名、保留某些 exit 元素),仍得拆开写
enter()和exit() - 注意:
.join("circle")中的字符串是 append 时的标签名,不是选择器;它不会去选已有 circle,而是新建
复杂交互场景下,手写三段式反而更清晰 —— 尤其当 enter 和 update 需要不同初始状态(如 enter 从 0 动画到目标值,update 直接跳转)。
最容易被忽略的一点:DOM 更新不是单次操作,而是一次“声明式意图”的执行过程。你写的每行 .data()、.enter()、.exit() 都在构建这个意图,D3 在最后一步才批量应用。中间任何一步中断(比如忘了 .merge() 或漏掉 .exit().remove()),都会让 DOM 状态和数据实际不一致,且这种不一致很难一眼看出来。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










