d3-fetch 数据加载 API 详解:d3 7.9.0 中一行代码加载并解析 CSV、JSON 与二进制文件
本文以当前仓库(d3 v7.9.0)中的 docs/d3-fetch.md 文档为主体,完整讲解 d3-fetch 模块提供的全套数据文件加载 API:如何基于 Fetch 用一行 await 加载文本、JSON、CSV/TSV、SVG/HTML/XML、图片与二进制文件,以及 init 与 row 两个可选参数的用法细节。读完本文,你不仅能掌握全部 11 个加载函数的签名与适用场景,还能理解它们在本仓库文档站点中被实际调用的方式,并知道 row 转换函数、CSP 限制、204/205 响应等边界情况的正确处理方式。
模块定位:在 Fetch 之上的便捷解析层
d3-fetch 是 d3 主包中的一个独立子模块,其官方定义是"在浏览器/平台标准 Fetch API 之上提供便捷的文件解析"(模块说明)。它解决的痛点是:原生 fetch 只返回 Response 对象,你还要手动 await response.text()、JSON.parse、解析 CSV……而 d3-fetch 把"取回 + 解析"合并成一步,例如加载文本文件:
const text = await d3.text("hello-world.txt"); // "Hello, world!"
加载并解析 CSV 文件:
const data = await d3.csv("hello-world.csv"); // [{"Hello": "world"}, …]
该模块内置对 JSON、CSV、TSV 三种格式的解析支持;其它格式(如纯文本、自定义结构)可以先用 d3.text 取回字符串再自行解析。文档中特别注明:d3-fetch 取代了旧的 d3-request 模块,因此在新代码中应优先使用本模块而非 d3.request / d3.json() 回调风格。
在当前仓库中可以确认它的装配方式:
- package.json 中主包
d3版本为7.9.0,依赖声明为"d3-fetch": "^3.0.1",说明本文档对应的 API 形态适用于 d3 v7 系列(基于 ES Module,函数返回 Promise,须配合async/await或.then使用); - src/index.js 中
export * from "d3-fetch";将全部 API 原样重导出,因此安装 d3 主包后即可直接使用d3.csv等函数,无需单独引入。
{
"name": "d3",
"version": "7.9.0",
"dependencies": { "d3-fetch": "^3.0.1", "…": "…" }
}
安装方式(只涉及标准 npm 安装,与仓库 package.json 声明一致):
npm install d3
全部加载 API 一览:blob、buffer、csv、dsv、html、image、json、svg、text、tsv、xml
官方文档 共定义了 11 个顶层函数,下面按功能分组完整覆盖。所有函数都接受 input(URL 字符串)与可选的 init 参数;除 dsv/csv/tsv 额外接受 row 外,init 会原样传递给底层 fetch 调用,因此可以使用任何 RequestInit 允许的字段(如 headers、method、signal 等)。
文本与 JSON
d3.text(input, init) — 获取指定 URL 的文本文件,Promise 解析为字符串。
const text = await d3.text("example.txt");
d3.json(input, init) — 获取并解析 JSON 文件。有一个重要边界行为:当服务器返回 204 No Content 或 205 Reset Content 状态码时,Promise 解析为 undefined(而不是空对象或抛错),在后续代码中应做判空处理。
const data = await d3.json("example.json");
分隔符文件:csv、tsv、dsv
d3.csv(input, init, row) — 等价于以逗号 , 为分隔符调用 d3.dsv。
const data = await d3.csv("example.csv");
d3.tsv(input, init, row) — 等价于以制表符 \t 为分隔符调用 d3.dsv。
const data = await d3.tsv("example.tsv");
d3.dsv(delimiter, input, init, row) — 通用入口,可指定任意单字符分隔符(管道符、分号等)。
const data = await d3.dsv(",", "example.csv");
dsv 的 row 转换函数(以及 init/row 的位置判定规则)是三个函数中最核心的实战细节,下一节单独展开。
结构化文档:html、svg、xml
这三个函数实现相同:先以 d3.text 取回文件内容,再用 DOMParser 解析为文档对象。区别仅在于解析时的文档类型:
d3.html(input, init) — 解析为 HTML 文档。
const document = await d3.html("example.html");
d3.svg(input, init) — 解析为 SVG 文档,配合 d3.select / 文档片段操作可把远程 SVG 挂入页面。
const document = await d3.svg("example.svg");
d3.xml(input, init) — 解析为 XML 文档。
const document = await d3.xml("example.xml");
图片与二进制
d3.image(input, init) — 加载图片(HTMLImageElement)。与其它函数不同,这里的 init 不是传给 fetch,而是在图片加载前把额外属性设置到 img 元素上。典型用途是开启匿名跨域请求(CORS image):
const image = await d3.image("https://example.com/image.png", {crossOrigin: "anonymous"});
d3.blob(input, init) — 将 URL 指向的二进制文件作为 Blob 获取。
const blob = await d3.blob("example.db");
d3.buffer(input, init) — 将二进制文件作为 ArrayBuffer 获取,适合直接交给 TypedArray 或文件解析库处理。
const buffer = await d3.buffer("example.db");
深入 dsv:init 与 row 的参数判定规则
d3.dsv(以及 d3.csv、d3.tsv)的签名是 (delimiter, input, init, row),其中 init 和 row 都是可选的。官方文档明确规定了当只传其中一个可选参数时的判定规则:
如果
init和row只指定了一个,若它是函数则解释为row转换函数,否则解释为init对象。
也就是说,以下两种写法完全等价,第二个参数位置既可以是配置对象也可以是转换函数:
// row 以函数身份占据第三个参数
const data = await d3.dsv(",", "example.csv", (d) => {
return {
year: new Date(+d.Year, 0, 1), // convert "Year" column to Date
make: d.Make,
model: d.Model,
length: +d.Length // convert "Length" column to number
};
});
// init 以对象身份占据第三个参数(例如带请求头)
const data = await d3.dsv(",", "example.csv", {headers: {"X-Token": "…"}});
row 转换函数对每一行被调用,接收当前行对象 d(以表头为属性名)、行索引 i 和列名数组;若返回 null 或 undefined,该行被过滤掉。这一语义与底层 d3-dsv 模块的 *dsv*.parse 完全一致——d3-fetch 的 d3.csv 本质是 fetch 拿到字符串后委托给 d3-dsv 的解析器。
row 函数与 d3.autoType 的组合
官方 d3-dsv 文档 提供了现成的行转换函数 d3.autoType:对每个字段先 trim,再按"空→null、"true"/"false"→布尔、"NaN"→NaN、可转数字→数字、ISO 日期串→Date、否则保持字符串"的规则推断类型。作为 row 传入时,一行 CSV 即可得到类型正确的对象:
const data = await d3.csv("example.csv", d3.autoType);
需要手动控制转换(例如把年份列转成 Date、把长度列转成数字)时,就自行编写 row 函数,如上节示例。注意官方对强制转换的建议:优先用 + 或 Number 而非 parseInt/parseFloat(更快但更严格,"30px" 用 + 会得到 NaN)。
两个需要注意的实现细节
- 列名顺序与
columns属性:解析结果的数组额外带有columns属性(按输入顺序的列名),与Object.keys的迭代顺序不同;若存在重名列,parse语义下每个名称只保留最后一个值,此时应改用parseRows思路处理(见 docs/d3-dsv.md)。 - Content Security Policy:
*dsv*.parse因使用动态代码生成来加速解析,要求script-src中含unsafe-eval;在严格 CSP 环境下可改用parseRows系列或自行解析(d3-dsv 文档说明)。
本仓库中的真实调用:文档站点如何加载数据
当前仓库本身就是一组 d3-fetch 的活示例——文档站点(VitePress,数据文件放在 docs/public/data/ 下)在多篇文档中直接调用这些 API:
用 d3.csv 加载 CSV 并自动转型(docs/d3-shape/stack.md):
d3.csv("../data/riaa-us-revenue.csv", d3.autoType).then((data) => (riaa.value = data));
对应的真实数据文件 docs/public/data/riaa-us-revenue.csv 结构为:
format,group,year,revenue
8 - Track,Tape,1973-01-01,2815.68
8 - Track,Tape,1974-01-01,2848.01
可见 d3.autoType 会把 year 列(ISO 日期串 1973-01-01)推断为 Date、revenue 列推断为数字,这正是上一节 autoType 规则的实际体现。注意这里用的是相对当前 HTML 页面的路径(../data/…),而 d3.csv("lost.csv", d3.autoType)(docs/d3-shape/pie.md)则是相对于页面所在的短路径——URL 解析遵循浏览器标准 URL 规则,写代码时要先确认文档页面位于哪个目录。
用 d3.json 加载 JSON(docs/components/ExampleDisjointForce.vue):
const dataPromise = d3.json("https://static.observableusercontent.com/files/e3680d5f…");
仓库自带的 docs/public/data/volcano.json({"width":87,"height":61,"values":[…]} 结构的二维数值矩阵)就是典型的 d3.json 消费对象,配合 d3.contours 或色标渲染等高线。
以上调用同时印证了 d3-fetch 与文档体系的分层关系:d3-fetch 负责"取回 + 解析",解析后的数组/对象再交给 d3-scale、d3-shape 等模块做可视化,这也是 d3 主包 src/index.js 将各子模块并列重导出的设计意图。
进阶用法:init 透传与错误处理
用 init 透传 fetch 配置
除 d3.image 外,所有函数的 init 都原样透传给底层 fetch,因此可以携带自定义请求头、控制请求方法、传入 AbortSignal 等:
// 携带鉴权头加载私有 JSON
const data = await d3.json("/api/report.json", {headers: {"Authorization": "Bearer …"}});
// 可取消的请求
const controller = new AbortController();
const textPromise = d3.text("large.txt", {signal: controller.signal});
controller.abort(); // 中止请求
空响应的特殊约定
再次强调 d3.json 的边界行为:服务器返回 204 No Content 或 205 Reset Content 时,Promise 解析为 undefined。健壮的数据加载代码通常写成:
const data = (await d3.json("example.json")) ?? [];
其余非 2xx 状态会走 fetch 的标准 reject 路径(网络错误、HTTP 错误由你自行 .catch 或 try/catch 处理)。
选型速查
| 目标格式 | 推荐函数 | 返回类型 | 备注 |
|---|---|---|---|
| 纯文本 | d3.text |
string |
其它格式的兜底取数方式 |
| JSON | d3.json |
对象/数组 | 204/205 时解析为 undefined |
| CSV / TSV / 其它 DSV | d3.csv / d3.tsv / d3.dsv |
带 columns 的行对象数组 |
支持 row 转换,配合 d3.autoType 转型 |
| HTML / SVG / XML | d3.html / d3.svg / d3.xml |
文档对象 | 先 text 后 DOMParser 解析 |
| 图片 | d3.image |
HTMLImageElement |
init 用于设置 img 属性(如 crossOrigin) |
| 二进制文件 | d3.blob / d3.buffer |
Blob / ArrayBuffer |
交给 TypedArray、文件解析库或 <a download> |
小结
d3-fetch 以极小的 API 面(11 个函数)覆盖了 d3 数据管道的前端——"把数据取回来并解析成可用的 JavaScript 结构"。掌握本文的三个要点即可应对绝大多数场景:所有加载函数都返回 Promise、init 除 d3.image 外均可透传给 fetch、d3.csv/tsv/dsv 的第三个参数按"函数即 row、对象即 init"的自动判定规则工作。完整的逐函数说明见 docs/d3-fetch.md,DSV 解析细节(parseRows、format、autoType、BOM 处理)见 docs/d3-dsv.md,真实数据样例可参考 docs/public/data/riaa-us-revenue.csv 与 docs/public/data/volcano.json。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00