news 2026/9/7 5:04:44

D3 v7 快速上手:CDN、npm 与 React/Svelte 集成的完整接入指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
D3 v7 快速上手:CDN、npm 与 React/Svelte 集成的完整接入指南

D3 v7 快速上手:CDN、npm 与 React/Svelte 集成的完整接入指南

【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3

D3(Data-Driven Documents)是一个运行在任何 JavaScript 环境中的数据可视化底层库。本篇基于官方入门文档 getting-started.md 与仓库源码,带你完整走通 D3 v7 的四种接入方式(在线环境、原生 HTML、npm、React/Svelte),并解释每种方式背后的模块组织与打包机制,读完即可在自己的项目中复制一套可运行的空白图表骨架。

一、D3 的运行形态:30 个子模块的聚合层

理解 D3 接入方式之前,先理解 D3 包本身的结构。从 package.json 可以看到,当前仓库版本为7.9.0"type": "module",要求 Node >= 12),dependencies中声明了 30 个子模块:d3-arrayd3-axisd3-brushd3-chordd3-colord3-contourd3-delaunayd3-dispatchd3-dragd3-dsvd3-eased3-fetchd3-forced3-formatd3-geod3-hierarchyd3-interpolated3-pathd3-polygond3-quadtreed3-randomd3-scaled3-scale-chromaticd3-selectiond3-shaped3-timed3-time-formatd3-timerd3-transitiond3-zoom

D3 主包并不重复实现这些功能,src/index.js 仅由 30 行export * from组成,把每个子模块的全部导出平铺到d3命名空间下;bundle.js 再补上从package.json导出的version字段。这也解释了为什么入门文档中d3.scaleUtc()d3.axisBottom()d3.line()都能直接从d3根对象访问。

测试用例 test/d3-test.js 用一条自动化断言守住了这一契约:遍历package.jsondependencies,动态import每个子模块,并断言其每个导出(除version外)都出现在d3命名空间中——从源码结构看,D3 主包的角色就是“全量子模块的再导出(re-export)层”,而不是独立的功能实现。

这个结构直接决定了后文的三种加载策略:可以整包引入(获得全部 30 个子模块的符号),也可以只从 CDN 或 npm 单独引入某个子模块。

二、第一张图:比例尺 + 坐标轴的空白图表骨架

无论使用哪种加载方式,入门文档给出的第一个示例都是同一个:用d3.create创建一个 640×400 的 SVG 容器,声明 x/y 两个比例尺,再用d3.axisBottom/d3.axisLeft挂上坐标轴,得到一个可以填充数据的空白图表。这也是文档内交互组件 ExampleBlankChart.vue 中实际渲染的代码。

核心代码(Observable 单元形式,完整继承自原文档):

{ // Declare the chart dimensions and margins. const width = 640; const height = 400; const marginTop = 20; const marginRight = 20; const marginBottom = 30; const marginLeft = 40; // Declare the x (horizontal position) scale. const x = d3.scaleUtc() .domain([new Date("2023-01-01"), new Date("2024-01-01")]) .range([marginLeft, width - marginRight]); // Declare the y (vertical position) scale. const y = d3.scaleLinear() .domain([0, 100]) .range([height - marginBottom, marginTop]); // Create the SVG container. const svg = d3.create("svg") .attr("width", width) .attr("height", height); // Add the x-axis. svg.append("g") .attr("transform", `translate(0,${height - marginBottom})`) .call(d3.axisBottom(x)); // Add the y-axis. svg.append("g") .attr("transform", `translate(${marginLeft},0)`) .call(d3.axisLeft(y)); // Return the SVG element. return svg.node(); }

几个值得注意的细节:

  • 边距约定(margin convention):x 轴的范围是[marginLeft, width - marginRight],y 轴是[height - marginBottom, marginTop](注意 y 轴起点在下、终点在上,符合 SVG 坐标系),坐标轴<g>通过transform="translate(...)"平移到边距边界上;
  • d3.create("svg")创建的是一个尚未插入文档的 SVG 元素,需要显式返回(Observable)或container.append(svg.node())(原生 HTML);
  • 该示例同时涉及三个子模块:d3-scale(scaleUtc/scaleLinear)、d3-selection(create/append/call)、d3-axis(axisBottom/axisLeft)。

2.1 在线体验:Observable 环境

官方文档推荐的入门路径是在 Observable 笔记本中使用:D3 作为 Observable 标准库的一部分默认可用,只需让单元返回生成的 DOM 元素即可渲染。除了上面的空白图表,文档列出了五个可 fork 的入门模板:面积图(Area chart)、柱状图(Bar chart)、环形图(Donut chart)、直方图(Histogram)、折线图(Line chart),并在 Observable 的 D3 gallery 中收录了数百个可 fork 的笔记本作为起步参考。点击+新建单元并输入 “d3” 可以过滤出内置的 D3 片段,Observable 还提供样例数据集、CSV/JSON 上传等便利功能供练习。

2.2 原生 HTML:三种加载方式

在原生 HTML 页面中,文档给出三种等价写法,示例逻辑与上面的空白图表完全一致,区别仅在 D3 的引入方式。

方式一:ESM + CDN(文档推荐)

<!DOCTYPE html> <div id="container"></div> <script type="module"> import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm"; // Declare the chart dimensions and margins. const width = 640; const height = 400; const marginTop = 20; const marginRight = 20; const marginBottom = 30; const marginLeft = 40; // Declare the x (horizontal position) scale. const x = d3.scaleUtc() .domain([new Date("2023-01-01"), new Date("2024-01-01")]) .range([marginLeft, width - marginRight]); // Declare the y (vertical position) scale. const y = d3.scaleLinear() .domain([0, 100]) .range([height - marginBottom, marginTop]); // Create the SVG container. const svg = d3.create("svg") .attr("width", width) .attr("height", height); // Add the x-axis. svg.append("g") .attr("transform", `translate(0,${height - marginBottom})`) .call(d3.axisBottom(x)); // Add the y-axis. svg.append("g") .attr("transform", `translate(${marginLeft},0)`) .call(d3.axisLeft(y)); // Append the SVG element. container.append(svg.node()); </script>

方式二:UMD + CDN

UMD 包以普通<script>加载时会挂出全局d3对象,适合无法使用 ES 模块的旧环境:

<!DOCTYPE html> <div id="container"></div> <script src="https://cdn.jsdelivr.net/npm/d3@7"></script> <script type="module"> // Declare the chart dimensions and margins. const width = 640; const height = 400; const marginTop = 20; const marginRight = 20; const marginBottom = 30; const marginLeft = 40; // Declare the x (horizontal position) scale. const x = d3.scaleUtc() .domain([new Date("2023-01-01"), new Date("2024-01-01")]) .range([marginLeft, width - marginRight]); // Declare the y (vertical position) scale. const y = d3.scaleLinear() .domain([0, 100]) .range([height - marginBottom, marginTop]); // Create the SVG container. const svg = d3.create("svg") .attr("width", width) .attr("height", height); // Add the x-axis. svg.append("g") .attr("transform", `translate(0,${height - marginBottom})`) .call(d3.axisBottom(x)); // Add the y-axis. svg.append("g") .attr("transform", `translate(${marginLeft},0)`) .call(d3.axisLeft(y)); // Append the SVG element. container.append(svg.node()); </script>

方式三:UMD + 本地文件(离线场景)

<!DOCTYPE html> <div id="container"></div> <script src="d3.js"></script> <script type="module"> // Declare the chart dimensions and margins. const width = 640; const height = 400; const marginTop = 20; const marginRight = 20; const marginBottom = 30; const marginLeft = 40; // Declare the x (horizontal position) scale. const x = d3.scaleUtc() .domain([new Date("2023-01-01"), new Date("2024-01-01")]) .range([marginLeft, width - marginRight]); // Declare the y (vertical position) scale. const y = d3.scaleLinear() .domain([0, 100]) .range([height - marginBottom, marginTop]); // Create the SVG container. const svg = d3.create("svg") .attr("width", width) .attr("height", height); // Add the x-axis. svg.append("g") .attr("transform", `translate(0,${height - marginBottom})`) .call(d3.axisBottom(x)); // Add the y-axis. svg.append("g") .attr("transform", `translate(${marginLeft},0)`) .call(d3.axisLeft(y)); // Append the SVG element. container.append(svg.node()); </script>

说明:官方文档页面上的d3.v7.js/d3.v7.min.js下载链接指向由构建流程生成的 UMD 包——prebuild.sh 会在文档构建时把dist/d3.jsdist/d3.min.js复制为docs/public/d3.v7.js/docs/public/d3.v7.min.js,因此这两个文件并不直接提交在源码树中。调试时使用非压缩版,生产环境使用压缩版以获得更快的加载性能。

UMD 全局d3从何而来?从 rollup.config.js 可以看到,构建产物dist/d3.js采用format: "umd"、全局变量名为d3,并附加了版本与版权 banner;同一配置还额外产出dist/d3.mjs(ESM 格式)和dist/d3.min.js(经 terser 压缩)。package.json 中的files字段确认发布包携带dist/d3.jsdist/d3.min.jsjsdelivr/unpkg字段与exports["umd"]都指向dist/d3.min.js——这就是上述 CDN 与 UMD 加载方式背后的产物。

2.3 只加载需要的子模块

如果只需要力导向图,不必引入整个 d3 聚合包,可以直接从 CDN 按需导入单个子模块的具名导出:

<script type="module"> import {forceSimulation, forceCollide, forceX} from "https://cdn.jsdelivr.net/npm/d3-force@3/+esm"; const nodes = [{}, {}]; const simulation = forceSimulation(nodes) .force("x", forceX()) .force("collide", forceCollide(5)) .on("tick", () => console.log(nodes[0].x)); </script>

注意这里导入的是独立的d3-force@3包而不是d3@7,子模块在 npm 上按各自的 v3 线发布(与主包 v7 并存),各子模块的最低版本约束以 package.json 中的dependencies为准。

三、从 npm 安装

如果你的应用基于 Node 构建(Vite、webpack 等打包环境),用任意包管理器安装整包:

# yarn yarn add d3
# npm npm install d3
# pnpm pnpm add d3

安装后有三种导入粒度:

// 1. 整体导入:获得 30 个子模块的全部符号(最常见) import * as d3 from "d3"; // 2. 具名导入:只取需要的符号,便于打包器摇树 import {select, selectAll} from "d3"; // 3. 子模块直连:直接依赖 d3-array 等独立包 import {mean, median} from "d3-array";

从源码结构看,package.json 将main/module都指向src/index.js,所以包管理器解析到的入口正是那个 30 行再导出文件,三种导入方式最终拿到的是同一套符号表。TypeScript 类型声明由社区维护的 DefinitelyTyped 库提供(@types/d3),文档未将其内置于包中,按需安装即可。

四、D3 在 React 中

D3 的模块大致分两类:

  • 不触碰 DOM 的纯计算模块(d3-scale、d3-array、d3-interpolate、d3-format 等)——在 React 中与普通模块无差别,可以直接在 JSX 里做纯声明式渲染;
  • 操作 selection 的模块(d3-selection、d3-transition、d3-axis)——直接改写真实 DOM,会与 React 的虚拟 DOM 冲突,需要借助 ref +useEffect把 D3 的写入限制在 React 不管理的节点内。

模式一:纯声明式(无 DOM 操作)。下面这个折线图组件只用了比例尺与 d3-shape 的line,SVG 元素全部由 React 渲染:

import * as d3 from "d3"; export default function LinePlot({ data, width = 640, height = 400, marginTop = 20, marginRight = 20, marginBottom = 20, marginLeft = 20 }) { const x = d3.scaleLinear([0, data.length - 1], [marginLeft, width - marginRight]); const y = d3.scaleLinear(d3.extent(data), [height - marginBottom, marginTop]); const line = d3.line((d, i) => x(i), y); return ( <svg width={width} height={height}> <path fill="none" stroke="currentColor" strokeWidth="1.5" d={line(data)} /> <g fill="white" stroke="currentColor" strokeWidth="1.5"> {data.map((d, i) => (<circle key={i} cx={x(i)} cy={y(d)} r="2.5" />))} </g> </svg> ); }

模式二:ref + useEffect(需要 DOM 操作的坐标轴)。给两个<g>挂 ref,在 effect 里把 D3 选集交给坐标轴:

import * as d3 from "d3"; import {useRef, useEffect} from "react"; export default function LinePlot({ data, width = 640, height = 400, marginTop = 20, marginRight = 20, marginBottom = 30, marginLeft = 40 }) { const gx = useRef(); const gy = useRef(); const x = d3.scaleLinear([0, data.length - 1], [marginLeft, width - marginRight]); const y = d3.scaleLinear(d3.extent(data), [height - marginBottom, marginTop]); const line = d3.line((d, i) => x(i), y); useEffect(() => void d3.select(gx.current).call(d3.axisBottom(x)), [gx, x]); useEffect(() => void d3.select(gy.current).call(d3.axisLeft(y)), [gy, y]); return ( <svg width={width} height={height}> <g ref={gx} transform={`translate(0,${height - marginBottom})`} /> <g ref={gy} transform={`translate(${marginLeft},0)`} /> <path fill="none" stroke="currentColor" strokeWidth="1.5" d={line(data)} /> <g fill="white" stroke="currentColor" strokeWidth="1.5"> {data.map((d, i) => (<circle key={i} cx={x(i)} cy={y(d)} r="2.5" />))} </g> </svg> ); }

关键点:useEffect的依赖数组传入[gx, x]/[gy, y],数据变化导致比例尺重建时坐标轴随之重绘;而<path><circle>仍由 React 声明,D3 只负责d3.axisBottom/d3.axisLeft内部生成的刻度与标签——两者各管各的 DOM 子树,互不覆盖。

五、D3 在 Svelte 中

Svelte 的策略与 React 相同:优先只用不操作 DOM 的模块做纯渲染,需要 DOM 操作时再借bind:this把节点交给 D3。

模式一:纯声明式折线图(使用 d3-shape 与 d3-scale):

<script> import * as d3 from 'd3'; export let data; export let width = 640; export let height = 400; export let marginTop = 20; export let marginRight = 20; export let marginBottom = 20; export let marginLeft = 20; $: x = d3.scaleLinear([0, data.length - 1], [marginLeft, width - marginRight]); $: y = d3.scaleLinear(d3.extent(data), [height - marginBottom, marginTop]); $: line = d3.line((d, i) => x(i), y); </script> <svg width={width} height={height}> <path fill="none" stroke="currentColor" stroke-width="1.5" d={line(data)} /> <g fill="white" stroke="currentColor" stroke-width="1.5"> {#each data as d, i} <circle key={i} cx={x(i)} cy={y(d)} r="2.5" /> {/each} </g> </svg>

模式二:响应式语句驱动动态坐标轴。Svelte 的$:响应式语句与 D3 的数据联结(data join)天然契合——数据一变,语句重算,坐标轴自动更新:

<script> import * as d3 from 'd3'; export let data; export let width = 640; export let height = 400; export let marginTop = 20; export let marginRight = 20; export let marginBottom = 30; export let marginLeft = 40; let gx; let gy; $: x = d3.scaleLinear([0, data.length - 1], [marginLeft, width - marginRight]); $: y = d3.scaleLinear(d3.extent(data), [height - marginBottom, marginTop]); $: line = d3.line((d, i) => x(i), y); $: d3.select(gy).call(d3.axisLeft(y)); $: d3.select(gx).call(d3.axisBottom(x)); </script> <svg width={width} height={height}> <g bind:this={gx} transform={`translate(0,${height - marginBottom})`} /> <g bind:this={gy} transform={`translate(${marginLeft},0)`} /> <path fill="none" stroke="currentColor" stroke-width="1.5" d={line(data)} /> <g fill="white" stroke="currentColor" stroke-width="1.5"> {#each data as d, i} <circle key={i} cx={x(i)} cy={y(d)} r="2.5" /> {/each} </g> </svg>

与 React 版对比:Svelte 不需要useEffect来声明副作用边界,bind:this+$:语句本身就承担了这个职责,因此框架内使用 D3 的“DOM 操作型”模块时,Svelte 的样板代码更少。

六、选型小结与后续路径

场景推荐方式依据
快速试验、教学演示Observable 在线笔记本D3 默认内置于其标准库,单元返回 DOM 即渲染
静态页面、一次性嵌入ESM + CDN(d3@7/+esm官方推荐;无构建步骤,<script type="module">直用
旧环境 / 无模块支持UMD + CDN(全局d3dist/d3.js为 UMD 格式,加载即挂全局
离线 / 内网环境本地 UMD 文件非压缩版调试、压缩版生产
Node 构建应用npm install d3+ ES 导入入口即 src/index.js 再导出层
React / Svelte纯计算模块声明式渲染;selection 类模块走 ref /bind:this避免 D3 与虚拟 DOM 争抢节点

入门之后,可按主题沿仓库文档深入:选择与数据联结见 d3-selection/selecting.md 与 d3-selection/joining.md,比例尺体系见 d3-scale.md 及其下的 band/linear/ordinal 等专题文档,图形生成器见 d3-shape/line.md 等;完整 API 总览可参考 API.md。由于 D3 v7 是纯 ESM 发布(type: "module"),若你的运行环境不支持原生 ES 模块或import语法,需要借助 UMD 包或自行使用打包工具做转译——这是选择加载方式时最核心的兼容性前提。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 5:04:26

作业指导书管理系统提升效率,如何选择最合适的方案?

1. 引言作业指导书&#xff08;SOP、WI、操作规范&#xff09;是生产制造、设备运维、质量管理等场景中连接标准与执行的关键载体。很多企业早期用 Word、Excel 或共享盘管理作业指导书&#xff0c;文件散落在不同电脑和网盘里&#xff0c;版本混乱、审批依赖邮件、现场员工难以…

作者头像 李华
网站建设 2026/9/7 4:59:02

基于SpringBoot+vue的Web的影视资源管理系统(毕业设计项目源码+文档)

温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台…

作者头像 李华
网站建设 2026/9/7 4:58:47

基于Handsontable构建Excel风格在线编辑表格的实践与优化

简介&#xff1a;Handsontable是一套基于JavaScript的Excel风格表格交互库&#xff0c;专为HTML前端页面提供类似Excel的数据网格编辑能力&#xff0c;面向需要在网页中实现复杂表格录入、编辑与交互的前端开发者或数据后台开发人员。它兼容IE10、Firefox、Chrome、Safari和Ope…

作者头像 李华
网站建设 2026/9/7 4:58:26

FPGA软核实战:DE2板上运行OC8051并点亮LED

简介&#xff1a;面向FPGA与嵌入式学习者&#xff0c;这套基于Altera DE2开发板的OC8051点灯实验资源&#xff0c;完整演示了如何在DE2硬件平台上集成可综合的8051软核处理器&#xff0c;并通过自带LED测试程序验证运行效果。整个压缩包共1042个文件、约19.07MB&#xff0c;以完…

作者头像 李华
网站建设 2026/9/7 4:57:27

构建Lojban工具链:livla管道式架构与文本解析实践

简介&#xff1a;这套面向Lojban语言学习者和开发者的多功能工具组合&#xff0c;整合了解析器、搜索界面、词典软件与IRC机器人等组件&#xff0c;可通过Docker或podman快速部署到本地环境。压缩包内共2000个文件、约44.38MB&#xff0c;其中1613个mp3音频构成丰富的发音素材库…

作者头像 李华
网站建设 2026/9/7 4:57:19

EFT测试整改全攻略:从IEC 61000-4-4标准到共模干扰实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华