Vue+D3开发环形图:如何避免遍历未定义/空数据集报错
问题:Vue中使用D3实现环形图时出现“尝试遍历空数据集”错误
我正在用D3.js开发环形图Vue组件,把D3代码封装成独立JS函数导入到组件后,运行时出现“尝试遍历空数据集”错误。后续计划用SQL查询获取数据,相关代码如下:
我的JS文件
// index.js import * as d3 from "d3"; function PieChart(data, { name = ([x]) => x, // 从数据中获取分类标签 value = ([, y]) => y, // 从数据中获取数值 title, // 鼠标悬浮时的提示文本 width = 500, // 图表宽度(像素) height = 500, // 图表高度(像素) innerRadius = 0.25*Math.min(height,width), // 环形图内半径 outerRadius = Math.min(width, height) / 2, // 环形图外半径 labelRadius = (innerRadius * 0.2 + outerRadius * 0.8), // 标签位置半径 format = ",", // 数值格式化规则 names, // 分类名称集合 colors, // 分类颜色集合 stroke = innerRadius > 0 ? "none" : "white", // 扇区间隔描边 strokeWidth = 1, // 描边宽度 strokeLinejoin = "round", // 描边连接方式 padAngle = stroke === "none" ? 1 / outerRadius : 0, // 扇区间角度间隔 } = {}) { // 处理数据 const N = d3.map(data, name); const V = d3.map(data, value); const I = d3.range(N.length).filter(i => !isNaN(V[i])); // 去重分类名称 if (names === undefined) names = N; names = new d3.InternSet(names); // 自动选择配色方案 if (colors === undefined) colors = d3.schemeSpectral[names.size]; if (colors === undefined) colors = d3.quantize(t => d3.interpolateSpectral(t * 0.8 + 0.1), names.size); // 创建颜色映射比例尺 const color = d3.scaleOrdinal(names, colors); // 生成提示文本 if (title === undefined) { const formatValue = d3.format(format); title = i => `${N[i]}\n${formatValue(V[i])}`; } else { const O = d3.map(data, d => d); const T = title; title = i => T(O[i], i, data); } // 生成饼图数据与弧生成器 const arcs = d3.pie().padAngle(padAngle).sort(null).value(i => V[i])(I); const arc = d3.arc().innerRadius(innerRadius).outerRadius(outerRadius); const arcLabel = d3.arc().innerRadius(labelRadius).outerRadius(labelRadius); // 创建SVG容器 const svg = d3.create("svg") .attr("width", width) .attr("height", height) .attr("viewBox", [-width / 2, -height / 2, width, height]) .attr("style", "max-width: 100%; height: auto; height: intrinsic;"); // 绘制扇区 svg.append("g") .attr("stroke", stroke) .attr("stroke-width", strokeWidth) .attr("stroke-linejoin", strokeLinejoin) .selectAll("path") .data(arcs) .join("path") .attr("fill", d => color(N[d.data])) .attr("d", arc) .append("title") .text(d => title(d.data)); // 绘制标签 svg.append("g") .attr("font-family", "sans-serif") .attr("font-size", 10) .attr("text-anchor", "middle") .selectAll("text") .data(arcs) .join("text") .attr("transform", d => `translate(${arcLabel.centroid(d)})`) .selectAll("tspan") .data(d => { const lines = `${title(d.data)}`.split(/\n/); return (d.endAngle - d.startAngle) > 0.25 ? lines : lines.slice(0, 1); }) .join("tspan") .attr("x", 0) .attr("y", (_, i) => `${i * 1.1}em`) .attr("font-weight", (_, i) => i ? null : "bold") .text(d => d); return Object.assign(svg.node(), {scales: {color}}); } export default PieChart;
我的DoughnutChart组件
<template> <div id="chart"> {{ PieChart() }} </div> </template> <script> const data = [{ name : "toto", value: 10 }, { name : "tato", value: 10 }, { name : "tota", value: 10 }]; PieChart( data, { name: d => d.name, value: d => Math.round(100*d.value/30), width: 400, height: 400 }) export default { name: 'DoughnutChart', methods : { PieChart, } } import PieChart from './index.js'; </script>
我的视图组件
<template> <DoughnutChart></DoughnutChart> </template> <script> import DoughnutChart from '@/components/Charts/DoughnutChart/DoughnutChart.vue'; export default { name: 'myBudgetView', components: { DoughnutChart }, } </script> <style scoped> #chart{ background-color: white; border-radius: 10px; padding: 20px; margin: 20px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); width: 75%; height: 75%; } </style>
我预期实现的环形图如下:
问题原因与修复方案
1. 核心错误点
- 组件中代码执行顺序错误:先调用
PieChart函数,后导入该函数,导致调用时函数未定义 - 模板中直接渲染
{{ PieChart() }}会把DOM对象转成字符串显示,无法正确挂载SVG - 未处理空数据场景,导致数据集为空时触发遍历错误
2. 修复后的DoughnutChart组件
<template> <div ref="chart"></div> </template> <script> import PieChart from './index.js'; export default { name: 'DoughnutChart', data() { return { chartData: [] }; }, mounted() { // 模拟从SQL获取数据,后续替换为真实接口请求 this.chartData = [ { name: "toto", value: 10 }, { name: "tato", value: 10 }, { name: "tota", value: 10 } ]; this.renderChart(); }, watch: { // 监听数据变化,自动重绘图表 chartData: { deep: true, handler() { this.renderChart(); } } }, methods: { renderChart() { // 清空容器,避免重复渲染 this.$refs.chart.innerHTML = ''; // 空数据时直接返回 if (this.chartData.length === 0) return; // 生成图表并挂载到容器 const svg = PieChart(this.chartData, { name: d => d.name, value: d => Math.round(100 * d.value / 30), width: 400, height: 400 }); this.$refs.chart.appendChild(svg); } } } </script>
3. D3函数的空数据兼容优化
在PieChart函数开头添加空数据判断,避免遍历空数据集:
function PieChart(data, { // 原有配置项 } = {}) { // 新增空数据处理 if (!data || data.length === 0) { const svg = d3.create("svg") .attr("width", width) .attr("height", height) .attr("viewBox", [-width / 2, -height / 2, width, height]) .attr("style", "max-width: 100%; height: auto; height: intrinsic;"); svg.append("text") .attr("text-anchor", "middle") .attr("dy", "0.35em") .text("暂无数据"); return svg.node(); } // 原有逻辑代码 }
内容的提问来源于stack exchange,提问作者KarljoY973
相关产品推荐
相关产品推荐

