尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

读懂 D3:Data-Driven Documents 的低层工具箱设计哲学与工程实现

读懂 D3:Data-Driven Documents 的低层工具箱设计哲学与工程实现 读懂 D3Data-Driven Documents 的低层工具箱设计哲学与工程实现【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3D3Data-Driven Documents数据驱动文档是一个基于 Web 标准构建的免费开源 JavaScript 数据可视化库。本文以官方文档 docs/what-is-d3.md 为主线完整梳理D3 是什么、为什么是低层工具箱、为什么面向动态可视化这五个核心命题并结合当前仓库的 package.json、src/index.js、rollup.config.js 与测试源码说明 D3 7.x 版本当前为 7.9.0的 30 个模块如何组织、构建与分发读完你能建立起从概念到源码层面的 D3 全局认知。D3 是什么D3或 D3.js是一个用于数据可视化的免费、开源 JavaScript 库。它采用基于 Web 标准的低层设计为创作动态、数据驱动的图形提供了极大的灵活性。D3 十多年来支撑了大量具有开创性、屡获殊荣的可视化作品成为更高层级图表库的基础构件并在全球范围内培育了活跃的数据实践者社区。这一点在 README.md 的开头段落中有同样的表述。D3 的历史可以追溯到 2011 年由 Mike Bostock 创建他与 Jeff Heer、Vadim Ogievetsky 在斯坦福大学共同发表了 D3 学术论文。Jason Davies 在 2011 至 2013 年间对 D3 贡献巨大尤其是地理投影系统部分Philippe Rivière 自 2016 年起成为 D3 及其文档的主要贡献者。如今 Mike Bostock 与 Philippe Rivière 共同维护 D3 及其姊妹库 Observable Plot。社区相关的更多背景可以参考 docs/community.md。从当前仓库 package.json 可以看到几个关键事实包名为d3描述为 Data-Driven Documents当前版本7.9.0采用ISC许可证作者为 Mike Bostock声明type: module即 D3 本体是 ES Module且exports字段同时提供 UMD 入口./dist/d3.min.js与默认 ESM 入口./src/index.js这意味着 D3 同时面向script全局加载和现代模块系统两种使用方式运行环境要求 Node12。D3 是一个低层工具箱而不是图表库官方文档开宗明义D3 不是传统意义上的图表库它没有图表chart的概念。用 D3 可视化数据时你实际上是在组合一组原语primitives。以制作一张堆叠面积图stacked area chart为例涉及的原语组合包括原语用途对应文档CSV 解析器加载数据docs/d3-dsv.md时间比例尺time scale水平位置xdocs/d3-scale/time.md线性比例尺linear scale垂直位置ydocs/d3-scale/linear.md序数比例尺 分类色板颜色编码docs/d3-scale/ordinal.md、docs/d3-scale-chromatic/categorical.md堆叠布局stack layout排列数值docs/d3-shape/stack.md面积形状 线性曲线生成 SVG path 数据docs/d3-shape/area.md、docs/d3-shape/curve.md坐标轴axes说明位置编码docs/d3-axis.md选择selections创建 SVG 元素docs/d3-selection.md一次性消化这些内容确实不少但每个部件都可以独立使用你可以逐个学习后再拼装到一起。D3 不是单一的整体monolith而是一套 30 个相互独立的库模块组成的工具箱把它们捆绑在一起发行只是为了方便而非技术上的必要。从源码验证30 个模块打开 src/index.js整个文件只有 30 行再导出语句逐一对应 30 个子模块export * from d3-array; export * from d3-axis; export * from d3-brush; // ... 共 30 行 ... export * from d3-transition; export * from d3-zoom;这些再导出与 package.json 中dependencies列出的 30 个包完全对应每个子包都有独立的语义版本范围例如d3-array: ^3.2.4、d3-scale: ^4.0.2、d3-delaunay: ^6.0.4。也就是说d3包本身只是一个聚合壳真正的实现分散在各子模块仓库中。这个契约还有测试保障。test/d3-test.js 遍历package.json的每一个依赖模块动态导入后断言该模块的每个导出除version外都存在于d3命名空间下for (const moduleName in packageData.dependencies) { it(d3 exports everything from ${moduleName}, async () { const module await import(moduleName); for (const propertyName in module) { if (propertyName ! version) { assert(propertyName in d3, ${moduleName} exports ${propertyName}); } } }); }这解释了为什么你可以既写import * as d3 from d3也可以只装子包按需引入如import {mean, median} from d3-array见 docs/getting-started.md。另外值得注意的是本仓库文档站本身就是 D3 用法的实证docs/what-is-d3.md 的脚本部分使用d3.tree().nodeSize([1, 1])与d3.stratify().path(...)把 VitePress 侧边栏构建成一棵树再用Plot.tree渲染出 D3 文档结构的树状导航图是层级数据 树布局的典型小型应用。官方文档同时给出了一条务实建议除非你确实需要 D3 的低层控制能力否则可以考虑其高层姊妹库Observable Plot——同一个直方图D3 可能需要 50 行代码Plot 一行即可完成两者也可以组合使用。D3 是灵活的正因为 D3 没有上层图表抽象即使是一张基础图表也可能需要几十行代码。但反过来看所有部件都摆在你面前你对发生的一切拥有完全的控制权可以把可视化精确调整到想要的样子。D3 对你的数据没有任何默认表现——最终呈现的只是你自己写出的代码或从示例中复制来的代码。官方文档把 D3 定位为自己动手做一切的替代方案而不是高层图表库的替代品如果你不满意现有工具、正打算用 SVG、Canvas甚至 WebGL自己画图表那么 D3 工具箱里几乎肯定有能帮到你、又不束缚创造力的现成部件。D3 与 Web 标准协同工作D3 不引入任何新的图形表示方式而是直接配合 SVG、Canvas 等 Web 标准使用。D3这个名字是data-driven documents的缩写其中documents指表示网页内容的 DOMDocument Object Model标准。从源码结构看D3 的 30 个模块可以粗分为两类直接操作 DOM 的模块d3-selection、d3-transition、d3-axis、d3-brush、d3-drag、d3-zoom 等只操作数据的模块d3-scale、d3-shape、d3-array、d3-force、d3-hierarchy、d3-geo 等它们不触碰 DOM因此在任何 JavaScript 环境包括 React 组件渲染期中都可以安全使用。拥抱 Web 标准带来诸多好处可以用外部样式表改变图表外观甚至响应媒体查询响应式图表、深色模式可以用浏览器的调试器和元素检查器审查代码行为D3 是同步、命令式synchronous, imperative的求值模型——调用selection.attr会立即修改 DOM——相比带复杂异步运行时如虚拟 DOM diff的框架往往更容易调试。D3 也可以与 React、Vue、Svelte 等框架配合使用docs/getting-started.md 给出了完整建议不操作 DOM 的模块如 d3-scale、d3-array可以在框架里以纯声明式方式使用而操作选择的模块d3-selection、d3-transition、d3-axis会与框架的虚拟 DOM 竞争需要借助useRef/useEffectReact或响应式语句Svelte把真实 DOM 节点交给 D3 管理。D3 面向定制bespoke可视化D3 让事情变得可行但不一定容易——即便是本该简单的事情往往也不简单。借用 Amanda Cox 的话如果你认为为一根柱状图写一百行代码是完全正常的那就用 D3。适合需要最大表达力的定制可视化。例如《纽约时报》《The Pudding》这类媒体机构单张图形可能面向百万读者且有一支编辑团队共同推进视觉表达的边界不适合临时搭建内部仪表盘或一次性分析。不要被炫技级示例带偏——其中很多实现了巨大工程量。如果你的时间有限用更高层的工具如 Observable Plot往往能得到更好的可视化或分析结果。D3 面向动态可视化data joinD3 最具创新性的概念是数据连接data join给定一组数据和一组 DOM 元素data join 允许你为entering进入、updating更新、exiting退出三种状态的元素分别执行不同操作。如果你只做静态图表不动画、不响应交互这个概念可能显得费解甚至奇怪——因为静态场景确实用不到它。data join 存在的意义在于让你精确控制数据变化时发生什么并据此更新显示。这种直接控制带来两个好处——极高的更新性能只触碰需要变化的元素和属性无需对 DOM 做 diff以及状态之间平滑的动画过渡。D3 在动态、交互式可视化领域表现出色。最小可运行示例用 join 生成表格docs/d3-selection/joining.md 给出了经典的最小示例把一个数字矩阵渲染成 HTML 表格其中数据函数是恒等函数每行返回矩阵中对应的行const matrix [ [11975, 5871, 8916, 2868], [ 1951, 10048, 2060, 6171], [ 8010, 16145, 8090, 8045], [ 1013, 990, 940, 6907] ]; d3.select(body) .append(table) .selectAll(tr) .data(matrix) .join(tr) .selectAll(td) .data(d d) .join(td) .text(d d);几个关键机制均来自 docs/d3-selection/joining.mdselection.data(data, key)把数据绑定到所选元素返回update选择并在其上定义enter与exit选择。数据被赋值给元素后存储在__data__属性上因此数据是粘性的重新选择时依然可用key 函数默认按索引一一对应指定 key 函数后按字符串标识匹配重复 key 的元素进入 exit 选择、重复 key 的数据进入 enter 选择selection.join(enter, update, exit)是通用更新模式general update pattern的便捷替代字符串简写.join(circle)等价于enter enter.append(circle)update updateexit exit.remove()。对动态数据场景推荐用 key 函数最小化 DOM 变动。例如为不同数据项指定稳定的 name 作为 keyconst data [ {name: Locke, number: 4}, {name: Reyes, number: 8}, {name: Ford, number: 15}, // ... ]; d3.selectAll(div) .data(data, function(d) { return d ? d.name : this.id; }) .text(d d.number);此外join的三个回调内还可以创建 transition从而对 enter/update/exit 三个阶段分别做动画这是 D3 动态可视化精确控制每个阶段的又一延伸点详见 docs/d3-transition.md。工程实现D3 7.x 如何构建与分发理解了概念后可以再看一眼当前仓库如何把 30 个模块打包成发布产物这直接决定了你在项目中怎么装 D3。rollup.config.js 以 bundle.js 为入口输出三种产物到dist/UMD 版d3.jsformat: umd、全局名d3供script标签直接加载对应package.json中的exports.umd与unpkg/jsdelivr字段ESM 版d3.mjsformat: esm供现代打包器使用压缩版d3.min.js额外挂接 terser 插件保留InternMap、InternSet标识符不被混淆。package.json 的files字段只发布dist/d3.js、dist/d3.min.js与src/**/*.jsprepublishOnly脚本在发布前会重新执行rollup -c生成这三份产物。发布后npm install d3得到的是 ESM 入口./src/index.js即上一节那 30 行再导出而文档站的 prebuild.sh 会把构建好的dist/d3.js与dist/d3.min.js复制为docs/public/d3.v7.js/d3.v7.min.js供页面本地加载——这就是 docs/getting-started.md 中UMD local用法本地d3.js脚本标签的产物来源。一个可复制的最小起点docs/getting-started.md 提供了空白图表模板完整体现了声明比例尺 → 创建 SVG → 调用坐标轴的标准套路以 ESM CDN 为例!DOCTYPE html div idcontainer/div script typemodule import * as d3 from https://cdn.jsdelivr.net/npm/d37/esm; // 声明图表尺寸与边距。 const width 640; const height 400; const marginTop 20; const marginRight 20; const marginBottom 30; const marginLeft 40; // 声明 x水平位置比例尺。 const x d3.scaleUtc() .domain([new Date(2023-01-01), new Date(2024-01-01)]) .range([marginLeft, width - marginRight]); // 声明 y垂直位置比例尺。 const y d3.scaleLinear() .domain([0, 100]) .range([height - marginBottom, marginTop]); // 创建 SVG 容器。 const svg d3.create(svg) .attr(width, width) .attr(height, height); // 添加 x 轴。 svg.append(g) .attr(transform, translate(0,${height - marginBottom})) .call(d3.axisBottom(x)); // 添加 y 轴。 svg.append(g) .attr(transform, translate(${marginLeft},0)) .call(d3.axisLeft(y)); // 追加 SVG 元素。 container.append(svg.node()); /script在 Node 项目中则通过包管理器安装npm install d3/yarn add d3/pnpm add d3然后import * as d3 from d3也可以按符号引入import {select, selectAll} from d3或直接安装子模块import {mean, median} from d3-array。小结回到 docs/what-is-d3.md 的核心命题D3 是低层工具箱——30 个可独立使用的模块见 src/index.js通过组合原语而非调用图表 API完成可视化D3 是灵活的——没有默认表现层所有部件对你透明适合替代纯手工的 SVG/Canvas 开发D3 与 Web 协同——直接作用于 DOM 与 SVG/Canvas同步命令式模型便于调试可与 React、Vue、Svelte 配合D3 面向定制——表达力换代码量适合高价值、可精雕细琢的作品对一次性分析则偏重D3 面向动态——data join 的 enter/update/exit 三阶段模型是精确、高性能、可动画更新的核心机制。掌握了这五点再配合各模块文档docs/d3-*.md与官方示例就可以按需取用工具箱中的具体部件而不是被学完整个 D3的压力吓退。【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表