D3

D3 简介与快速上手

了解 D3 是什么、它和 ECharts 的区别,认识 SVG 画布并画出第一个数据驱动的柱状图。

🎯 引言

学会这篇文章,你能明白 D3 是什么、它和 ECharts 有什么区别,还会认识 D3 画图的“画布”SVG。最后我们会在 Vue3 项目中安装 D3,画出第一个由数据驱动的柱状图,并且把每个数据是怎么变成柱子的算给你看。


🧱 什么是 D3

D3 全称 Data-Driven Documents(数据驱动文档)。名字有点绕,拆开看就懂了:

  • 数据(Data):你手上的一组数字,比如一周的销量。
  • 文档(Documents):网页上的元素,比如矩形、圆形。
  • 驱动(Driven):让数据去决定元素长什么样——数值大,柱子就高。

所以 D3 做的事就一件:把数据和网页元素绑在一起,让数据变成看得见的图形


⚖️ D3 和 ECharts 怎么选

学过 ECharts 的同学会问:都是画图表,为什么要学 D3?

打个比方:ECharts 像成品家具,D3 像木工工具箱。成品家具买来就能用,但样式改不了多少;工具箱里有锯子刨子,什么家具都能做,只是要自己动手。

对比EChartsD3
定位现成的图表库底层可视化工具箱
用法写一个配置对象自己拼零件
上手难度较高
自由度只能在图表类型内调整任何图形、任何交互都能做
适合场景后台报表、常规业务图表定制大屏、创意可视化作品
选型建议:常规图表用 ECharts,ECharts 做不出来的定制需求用 D3。D3 能做出 ECharts 的所有图表,反过来却不行。

🧱 先认识 SVG:D3 的画布

D3 画出来的图形,其实都是普通的 SVG 标签。学 D3 之前,我们先看看“成品”长什么样。

下面这段 SVG 手写了两个柱子,不需要任何库,浏览器直接就能显示:

<svg width="300" height="300">
    <rect x="0" y="180" width="40" height="120" fill="#5470c6"></rect>
    <rect x="60" y="100" width="40" height="200" fill="#5470c6"></rect>
</svg>

rect 就是矩形,四个属性决定它的样子:

  • xy:矩形左上角的位置。
  • widthheight:宽和高。
  • fill:填充颜色。

有一个必须记住的点:SVG 的 y 轴是向下增长的,和数学课的坐标系正好相反:

(0,0) ───────────→ x 轴(越往右越大)
  │
  │   越往下,y 越大
  ▼
 y 轴

所以柱子要“从下往上长”,写法是:y(柱子顶部)= 画布高度 - 数值,height = 数值。比如画布高 300,数值 120,柱子顶部就在 y = 180,往下画 120 的高度,正好顶到画布底。

手写两个柱子还行,手写 100 个就是灾难。D3 的价值就是:我们给数据,它自动生成这些标签。

上面那段手写 SVG 的真实渲染效果(就是两个普通柱子,没有任何库参与):


🚀 安装 D3

当前课程环境使用 Node.js v22.0.0,安装的 D3 版本为 7.9.0。在 Vite 创建的 Vue3 项目中,执行:

npm install d3
如果你的 Node.js 版本太老,可能导致安装失败。建议先运行 node -v 确认版本,推荐使用 LTS 版本。

在代码中引入:

import * as d3 from 'd3';

这表示把 D3 的所有工具(选元素、算比例、做动画等)一次性引入,学习阶段这样写最省心。


🛠 第一个图表

目标:把 [120, 200, 150, 80, 70] 这 5 个数,变成 5 个高低不一的柱子。

views/FirstChart.vue
<template>
    <svg ref="svgRef" class="chart"></svg>
</template>

<script setup>
import { onMounted, ref } from 'vue';
import * as d3 from 'd3';

const svgRef = ref(null);

const data = [120, 200, 150, 80, 70];

onMounted(() => {
    // 1. 选中 svg 容器
    const svg = d3.select(svgRef.value);

    // 2. 绑定数据,为每个数据创建一个 rect
    svg.selectAll('rect')
        .data(data)
        .enter()
        .append('rect')
        .attr('x', (d, i) => i * 60) // 每个柱子间隔 60px
        .attr('y', (d) => 300 - d) // 柱子顶部 = 画布高度 - 数值
        .attr('width', 40)
        .attr('height', (d) => d) // 柱子高度就是数值
        .attr('fill', '#5470c6');
});
</script>

<style scoped>
.chart {
    width: 100%;
    height: 300px;
}
</style>

代码里的 (d, i) 是 D3 给每个数据项的“编号”:d 是当前数据,i 是它是第几个(从 0 开始)。我们把 5 个数据代进去,看看 D3 到底算出了什么:

数据 d索引 ix = i × 60y = 300 − dheight = d
12000180120
200160100200
1502120150150
80318022080
70424023070

对照一下:第一行 x=0, y=180, width=40, height=120,和我们前面手写的第一个 rect 一模一样!D3 没有魔法,它只是把这些重复计算自动化了。

下面是这段代码在页面上的真实渲染效果

再看最核心的三行代码:

svg.selectAll('rect') // 选中所有 rect(现在一个都没有)
    .data(data) // 绑定 5 个数据
    .enter() // 拿到“有数据但还没有元素”的部分
    .append('rect'); // 为每个数据补一个 rect

理解成“发书”就很好懂:数据是 5 本书,页面上的 rect 是 0 个学生。书比学生多,就需要“补招学生”——enter().append('rect') 就是补招的动作,每本书(数据)配一个学生(柱子)。

这个 .selectAll().data().enter().append() 是 D3 的万能公式,几乎所有图形都从它开始,下一篇会详细拆解。

两个最常见的踩坑点:
  1. 画图代码必须放在 onMounted 里执行,这时 svgRef.value 才真正指向 DOM 元素。
  2. SVG 容器必须设置高度。高度为 0 时代码不报错,但页面上什么都看不到。

🧾 小节总结

  • D3 = 数据驱动文档,做的事就是把数据变成网页上的图形。
  • 常规图表选 ECharts,定制需求选 D3,两者是互补关系。
  • SVG 的 y 轴向下增长,柱子写法是 y = 画布高度 - 数值height = 数值
  • 使用 npm install d3 安装,课程基于 Node.js v22.0.0 和 D3 7.9.0。
  • 万能公式:selectAll().data().enter().append();attr 回调中 d 是数据、i 是索引。

❓ 知识问答

Q1:D3 收费吗?

完全免费。它是 BSD 开源协议下的项目,个人和公司都可以免费商用。

Q2:D3 只能画 SVG 吗?

不是,D3 也能操作 Canvas 和普通 HTML 元素。但 SVG 适合绝大多数图表场景,本课程统一使用 SVG。

Q3:既然能手写 SVG,为什么还要 D3?

数据固定时手写确实可以。但真实项目中数据来自接口、随时变化,每次都手写 100 个标签不现实。D3 让图形跟着数据自动更新,这才是它的价值。

Q4:import * as d3 会不会打包体积很大?

全量引入确实会大一些。学习阶段推荐全量引入省心,项目上线时可以只引入用到的子模块(如 d3-selectiond3-scale)减小体积。


🧪 小练习

在上面第一个图表的基础上,完成下面的改造:

  1. 把数据改成 7 天的销量,数值自己填。
  2. 把柱子颜色改成 #91cc75
  3. 仿照文章里的表格,在纸上或控制台把每个柱子的 xyheight 算一遍,再和页面上的实际效果对照。
views/WeekChart.vue
<template>
    <svg ref="svgRef" class="chart"></svg>
</template>

<script setup>
import { onMounted, ref } from 'vue';
import * as d3 from 'd3';

const svgRef = ref(null);

onMounted(() => {
    const svg = d3.select(svgRef.value);

    // 请在这里编写代码
});
</script>

<style scoped>
.chart {
    width: 100%;
    height: 300px;
}
</style>

🎉 恭喜你已经画出第一个 D3 图表了!下一篇我们拆解“发书”的完整过程,学会数据变多、变少时怎么正确增删图形。