D3 简介与快速上手
🎯 引言
学会这篇文章,你能明白 D3 是什么、它和 ECharts 有什么区别,还会认识 D3 画图的“画布”SVG。最后我们会在 Vue3 项目中安装 D3,画出第一个由数据驱动的柱状图,并且把每个数据是怎么变成柱子的算给你看。
🧱 什么是 D3
D3 全称 Data-Driven Documents(数据驱动文档)。名字有点绕,拆开看就懂了:
- 数据(Data):你手上的一组数字,比如一周的销量。
- 文档(Documents):网页上的元素,比如矩形、圆形。
- 驱动(Driven):让数据去决定元素长什么样——数值大,柱子就高。
所以 D3 做的事就一件:把数据和网页元素绑在一起,让数据变成看得见的图形。
⚖️ D3 和 ECharts 怎么选
学过 ECharts 的同学会问:都是画图表,为什么要学 D3?
打个比方:ECharts 像成品家具,D3 像木工工具箱。成品家具买来就能用,但样式改不了多少;工具箱里有锯子刨子,什么家具都能做,只是要自己动手。
| 对比 | ECharts | D3 |
|---|---|---|
| 定位 | 现成的图表库 | 底层可视化工具箱 |
| 用法 | 写一个配置对象 | 自己拼零件 |
| 上手难度 | 低 | 较高 |
| 自由度 | 只能在图表类型内调整 | 任何图形、任何交互都能做 |
| 适合场景 | 后台报表、常规业务图表 | 定制大屏、创意可视化作品 |
🧱 先认识 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 就是矩形,四个属性决定它的样子:
x、y:矩形左上角的位置。width、height:宽和高。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 -v 确认版本,推荐使用 LTS 版本。在代码中引入:
import * as d3 from 'd3';
这表示把 D3 的所有工具(选元素、算比例、做动画等)一次性引入,学习阶段这样写最省心。
🛠 第一个图表
目标:把 [120, 200, 150, 80, 70] 这 5 个数,变成 5 个高低不一的柱子。
<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 | 索引 i | x = i × 60 | y = 300 − d | height = d |
|---|---|---|---|---|
| 120 | 0 | 0 | 180 | 120 |
| 200 | 1 | 60 | 100 | 200 |
| 150 | 2 | 120 | 150 | 150 |
| 80 | 3 | 180 | 220 | 80 |
| 70 | 4 | 240 | 230 | 70 |
对照一下:第一行 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 的万能公式,几乎所有图形都从它开始,下一篇会详细拆解。
- 画图代码必须放在
onMounted里执行,这时svgRef.value才真正指向 DOM 元素。 - 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-selection、d3-scale)减小体积。
🧪 小练习
在上面第一个图表的基础上,完成下面的改造:
- 把数据改成 7 天的销量,数值自己填。
- 把柱子颜色改成
#91cc75。 - 仿照文章里的表格,在纸上或控制台把每个柱子的
x、y、height算一遍,再和页面上的实际效果对照。
<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 图表了!下一篇我们拆解“发书”的完整过程,学会数据变多、变少时怎么正确增删图形。
