引言:为什么选择Chart.js与React结合
在现代Web开发中,数据可视化已成为不可或缺的技能。React作为前端框架的霸主,与Chart.js这个轻量级但功能强大的图表库结合,能够让我们快速构建响应式、交互性强的数据图表。本教程将从零基础开始,逐步深入,带你掌握在React项目中高效集成Chart.js的完整流程。
为什么选择Chart.js?
Chart.js具有以下优势:
轻量级
:核心库仅约60KB,gzip后更小
响应式
:自动适应容器大小变化
丰富的图表类型
:支持折线图、柱状图、饼图、雷达图等
高度可定制
:支持主题、颜色、动画等深度定制
良好的浏览器兼容性
:支持现代浏览器及IE11+
为什么需要React封装?
虽然Chart.js可以直接使用,但在React生态中,我们需要:
组件化
:将图表封装为可复用的React组件
状态管理
:与React状态系统无缝集成
性能优化
:避免不必要的重绘和内存泄漏
声明式编程
:符合React的声明式编程范式
第一部分:基础环境搭建
1.1 创建React项目
首先,我们需要一个React项目环境。推荐使用Create React App快速启动:
npx create-react-app chartjs-react-demo
cd chartjs-react-demo
1.2 安装Chart.js及相关依赖
在React项目中,我们通常使用
react-chartjs-2
这个官方推荐的React封装库:
npm install chart.js react-chartjs-2
# 或者使用yarn
yarn add chart.js react-chartjs-2
1.3 项目结构规划
为了保持代码整洁,建议创建以下目录结构:
src/
├── components/
│ ├── charts/
│ │ ├── LineChart.js
│ │ ├── BarChart.js
│ │ └── PieChart.js
│ └── ChartContainer.js
├── utils/
│ └── chartConfig.js
├── App.js
└── index.js
第二部分:基础图表实现
2.1 创建第一个折线图
让我们从最简单的折线图开始。创建
src/components/charts/LineChart.js
:
import React from ‘react’;
import { Line } from ‘react-chartjs-2’;
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from ‘chart.js’;
// 注册Chart.js组件
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
const LineChart = () => {
// 图表数据
const data = {
labels: [‘1月’, ‘2月’, ‘3月’, ‘4月’, ‘5月’, ‘6月’],
datasets: [
{
label: ‘销售额’,
data: [12000, 19000, 15000, 25000, 22000, 30000],
borderColor: ‘rgb(255, 99, 132)’,
backgroundColor: ‘rgba(255, 99, 132, 0.5)’,
tension: 0.4, // 曲线平滑度
fill: true, // 是否填充区域
},
],
};
// 图表配置
const options = {
responsive: true, // 响应式
maintainAspectRatio: false, // 不保持宽高比
plugins: {
legend: {
position: ‘top’, // 图例位置
},
title: {
display: true,
text: ‘月度销售趋势’,
},
tooltip: {
mode: ‘index’,
intersect: false,
},
},
scales: {
y: {
beginAtZero: true, // Y轴从0开始
ticks: {
callback: function(value) {
return ‘¥’ + value.toLocaleString(); // 格式化Y轴标签
},
},
},
},
};
return (
);
};
export default LineChart;
2.2 在App.js中使用图表
import React from ‘react’;
import LineChart from ‘./components/charts/LineChart’;
import ‘./App.css’;
function App() {
return (
React + Chart.js 实战演示
基础折线图
);
}
export default App;
2.3 添加基础样式
在
src/App.css
中添加一些基础样式:
.App {
text-align: center;
font-family: -apple-system, BlinkMacSystemFont, ‘Segoe UI’, ‘Roboto’, sans-serif;
}
.main-content {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.chart-section {
background: white;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.chart-section h2 {
margin-top: 0;
color: #333;
}
第三部分:高级图表类型与配置
3.1 柱状图(Bar Chart)
创建
src/components/charts/BarChart.js
:
import React from ‘react’;
import { Bar } from ‘react-chartjs-2’;
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from ‘chart.js’;
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
);
const BarChart = ({ data, options }) => {
// 默认数据
const defaultData = {
labels: [‘产品A’, ‘产品B’, ‘产品C’, ‘产品D’, ‘产品E’],
datasets: [
{
label: ‘销量’,
data: [65, 59, 80, 81, 56],
backgroundColor: [
‘rgba(255, 99, 132, 0.7)’,
‘rgba(54, 162, 235, 0.7)’,
‘rgba(255, 206, 86, 0.7)’,
‘rgba(75, 192, 192, 0.7)’,
‘rgba(153, 102, 255, 0.7)’,
],
borderColor: [
‘rgba(255, 99, 132, 1)’,
‘rgba(54, 162, 235, 1)’,
‘rgba(255, 206, 86, 1)’,
‘rgba(75, 192, 192, 1)’,
‘rgba(153, 102, 255, 1)’,
],
borderWidth: 1,
},
],
};
const defaultOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘top’,
},
title: {
display: true,
text: ‘产品销量统计’,
},
},
scales: {
y: {
beginAtZero: true,
},
},
};
return (
);
};
export default BarChart;
3.2 饼图(Pie Chart)
创建
src/components/charts/PieChart.js
:
import React from ‘react’;
import { Pie } from ‘react-chartjs-2’;
import {
Chart as ChartJS,
ArcElement,
Tooltip,
Legend,
} from ‘chart.js’;
ChartJS.register(ArcElement, Tooltip, Legend);
const PieChart = () => {
const data = {
labels: [‘Red’, ‘Blue’, ‘Yellow’, ‘Green’, ‘Purple’],
datasets: [
{
label: ‘# of Votes’,
data: [12, 19, 3, 5, 2],
backgroundColor: [
‘rgba(255, 99, 132, 0.7)’,
‘rgba(54, 162, 235, 0.7)’,
‘rgba(255, 206, 86, 0.7)’,
‘rgba(75, 192, 192, 0.7)’,
‘rgba(153, 102, 255, 0.7)’,
],
borderColor: [
‘rgba(255, 99, 132, 1)’,
‘rgba(54, 162, 235, 1)’,
‘rgba(255, 206, 86, 1)’,
‘rgba(75, 192, 192, 1)’,
‘rgba(153, 102, 255, 1)’,
],
borderWidth: 2,
},
],
};
const options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘right’,
},
title: {
display: true,
text: ‘投票分布’,
},
},
};
return (
);
};
export default PieChart;
3.3 雷达图(Radar Chart)
创建
src/components/charts/RadarChart.js
:
import React from ‘react’;
import { Radar } from ‘react-chartjs-2’;
import {
Chart as ChartJS,
RadialLinearScale,
PointElement,
LineElement,
Filler,
Tooltip,
Legend,
} from ‘chart.js’;
ChartJS.register(
RadialLinearScale,
PointElement,
LineElement,
Filler,
Tooltip,
Legend
);
const RadarChart = () => {
const data = {
labels: [‘速度’, ‘力量’, ‘技巧’, ‘耐力’, ‘敏捷’],
datasets: [
{
label: ‘选手A’,
data: [85, 90, 70, 80, 95],
fill: true,
backgroundColor: ‘rgba(255, 99, 132, 0.2)’,
borderColor: ‘rgb(255, 99, 132)’,
pointBackgroundColor: ‘rgb(255, 99, 132)’,
pointBorderColor: ‘#fff’,
pointHoverBackgroundColor: ‘#fff’,
pointHoverBorderColor: ‘rgb(255, 99, 132)’,
},
{
label: ‘选手B’,
data: [70, 85, 90, 75, 80],
fill: true,
backgroundColor: ‘rgba(54, 162, 235, 0.2)’,
borderColor: ‘rgb(54, 162, 235)’,
pointBackgroundColor: ‘rgb(54, 162, 235)’,
pointBorderColor: ‘#fff’,
pointHoverBackgroundColor: ‘#fff’,
pointHoverBorderColor: ‘rgb(54, 162, 235)’,
},
],
};
const options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘top’,
},
title: {
display: true,
text: ‘选手能力对比’,
},
},
scales: {
r: {
min: 0,
max: 100,
ticks: {
stepSize: 20,
},
},
},
};
return (
);
};
export default RadarChart;
第四部分:动态数据与状态管理
4.1 使用useState管理图表数据
创建一个动态图表组件,展示如何根据用户输入更新图表:
import React, { useState } from ‘react’;
import { Line } from ‘react-chartjs-2’;
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from ‘chart.js’;
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
const DynamicLineChart = () => {
const [chartData, setChartData] = useState({
labels: [‘周一’, ‘周二’, ‘周三’, ‘周四’, ‘周五’],
datasets: [
{
label: ‘访问量’,
data: [120, 190, 150, 250, 220],
borderColor: ‘rgb(75, 192, 192)’,
backgroundColor: ‘rgba(75, 192, 192, 0.5)’,
tension: 0.4,
},
],
});
const [inputValue, setInputValue] = useState(”);
// 添加新数据点
const addDataPoint = () => {
if (!inputValue) return;
const value = parseInt(inputValue, 10);
if (isNaN(value)) return;
const newLabels = […chartData.labels, `第${chartData.labels.length + 1}天`];
const newData = […chartData.datasets[0].data, value];
setChartData({
labels: newLabels,
datasets: [
{
…chartData.datasets[0],
data: newData,
},
],
});
setInputValue(”);
};
// 重置数据
const resetData = () => {
setChartData({
labels: [‘周一’, ‘周二’, ‘周三’, ‘周四’, ‘周五’],
datasets: [
{
label: ‘访问量’,
data: [120, 190, 150, 250, 220],
borderColor: ‘rgb(75, 192, 192)’,
backgroundColor: ‘rgba(75, 192, 192, 0.5)’,
tension: 0.4,
},
],
});
};
const options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘top’,
},
title: {
display: true,
text: ‘动态数据演示’,
},
},
scales: {
y: {
beginAtZero: true,
},
},
};
return (
placeholder=”输入数值”
style={{ marginRight: ’10px’, padding: ‘8px’ }}
/>
);
};
export default DynamicLineChart;
4.2 使用useEffect处理异步数据
展示如何从API获取数据并更新图表:
import React, { useState, useEffect } from ‘react’;
import { Line } from ‘react-chartjs-2’;
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from ‘chart.js’;
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
const ApiDataChart = () => {
const [chartData, setChartData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
// 模拟API调用
// 实际项目中替换为真实API地址
// const response = await fetch(‘https://api.example.com/stats’);
// const data = await response.json();
// 模拟延迟
await new Promise(resolve => setTimeout(resolve, 1000));
// 模拟返回数据
const mockData = {
labels: [‘2024-01’, ‘2024-02’, ‘2024-03’, ‘2024-04’, ‘2024-05’, ‘2024-06’],
values: [65, 78, 90, 81, 56, 102]
};
setChartData({
labels: mockData.labels,
datasets: [
{
label: ‘API数据’,
data: mockData.values,
borderColor: ‘rgb(255, 99, 132)’,
backgroundColor: ‘rgba(255, 99, 132, 0.5)’,
tension: 0.4,
},
],
});
setLoading(false);
} catch (err) {
setError(err.message);
setLoading(false);
}
};
fetchData();
}, []); // 空依赖数组,只在组件挂载时执行
const options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘top’,
},
title: {
display: true,
text: ‘API数据图表’,
},
},
};
if (loading) {
return
;
}
if (error) {
return
;
}
return (
);
};
export default ApiDataChart;
第五部分:性能优化与最佳实践
5.1 使用React.memo避免不必要的重渲染
import React, { memo } from ‘react’;
import { Line } from ‘react-chartjs-2’;
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from ‘chart.js’;
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
// 使用memo包裹组件,只有props变化时才重新渲染
const MemoizedLineChart = memo(({ data, options }) => {
console.log(‘Chart rendered’);
return (
);
}, (prevProps, nextProps) => {
// 自定义比较函数:只有数据真正变化时才更新
return JSON.stringify(prevProps.data) === JSON.stringify(nextProps.data);
});
export default MemoizedLineChart;
5.2 使用useRef和useCallback优化事件处理
import React, { useState, useCallback, useRef } from ‘react’;
import { Line } from ‘react-chartjs-2’;
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from ‘chart.js’;
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
const OptimizedChart = () => {
const [data, setData] = useState({
labels: [‘A’, ‘B’, ‘C’, ‘D’],
datasets: [
{
label: ‘数据’,
data: [10, 20, 30, 40],
borderColor: ‘rgb(75, 192, 192)’,
backgroundColor: ‘rgba(75, 192, 192, 0.5)’,
},
],
});
const chartRef = useRef(null);
// 使用useCallback缓存函数,避免每次渲染都创建新函数
const updateData = useCallback(() => {
setData(prevData => ({
…prevData,
datasets: [
{
…prevData.datasets[0],
data: prevData.datasets[0].data.map(v => v + Math.random() * 10),
},
],
}));
}, []);
const exportChart = useCallback(() => {
if (chartRef.current) {
// 获取图表canvas并导出为图片
const canvas = chartRef.current.canvas;
const url = canvas.toDataURL(‘image/png’);
const link = document.createElement(‘a’);
link.download = ‘chart.png’;
link.href = url;
link.click();
}
}, []);
const options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘top’,
},
title: {
display: true,
text: ‘优化后的图表’,
},
},
};
return (
);
};
export default OptimizedChart;
5.3 自定义Hooks封装图表逻辑
创建
src/hooks/useChartData.js
:
import { useState, useCallback } from ‘react’;
// 自定义Hook:管理图表数据
export const useChartData = (initialData) => {
const [data, setData] = useState(initialData);
const addDataPoint = useCallback((label, value) => {
setData(prevData => ({
labels: […prevData.labels, label],
datasets: [
{
…prevData.datasets[0],
data: […prevData.datasets[0].data, value],
},
],
}));
}, []);
const updateDataPoint = useCallback((index, newValue) => {
setData(prevData => {
const newData = […prevData.datasets[0].data];
newData[index] = newValue;
return {
…prevData,
datasets: [
{
…prevData.datasets[0],
data: newData,
},
],
};
});
}, []);
const resetData = useCallback(() => {
setData(initialData);
}, [initialData]);
const removeLastDataPoint = useCallback(() => {
setData(prevData => ({
labels: prevData.labels.slice(0, -1),
datasets: [
{
…prevData.datasets[0],
data: prevData.datasets[0].data.slice(0, -1),
},
],
}));
}, []);
return {
data,
addDataPoint,
updateDataPoint,
resetData,
removeLastDataPoint,
setData,
};
};
使用自定义Hook:
import React from ‘react’;
import { Line } from ‘react-chartjs-2’;
import { useChartData } from ‘../hooks/useChartData’;
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from ‘chart.js’;
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
const HookBasedChart = () => {
const initialData = {
labels: [‘初始1’, ‘初始2’, ‘初始3’],
datasets: [
{
label: ‘Hook数据’,
data: [10, 20, 30],
borderColor: ‘rgb(153, 102, 255)’,
backgroundColor: ‘rgba(153, 102, 255, 0.5)’,
tension: 0.4,
},
],
};
const {
data,
addDataPoint,
updateDataPoint,
resetData,
removeLastDataPoint,
} = useChartData(initialData);
const options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘top’,
},
title: {
display: true,
text: ‘使用自定义Hook的图表’,
},
},
};
return (
);
};
export default HookBasedChart;
第六部分:高级主题与自定义
6.1 自定义工具提示(Tooltip)
import React from ‘react’;
import { Line } from ‘react-chartjs-2’;
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from ‘chart.js’;
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
const CustomTooltipChart = () => {
const data = {
labels: [‘1月’, ‘2月’, ‘3月’, ‘4月’, ‘5月’],
datasets: [
{
label: ‘销售额’,
data: [12000, 19000, 15000, 25000, 22000],
borderColor: ‘rgb(255, 99, 132)’,
backgroundColor: ‘rgba(255, 99, 132, 0.5)’,
tension: 0.4,
},
],
};
const options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
tooltip: {
// 自定义工具提示内容
callbacks: {
label: function(context) {
let label = context.dataset.label || ”;
if (label) {
label += ‘: ‘;
}
if (context.parsed.y !== null) {
label += new Intl.NumberFormat(‘zh-CN’, {
style: ‘currency’,
currency: ‘CNY’,
}).format(context.parsed.y);
}
return label;
},
title: function(context) {
return `日期: ${context[0].label}`;
},
},
// 自定义工具提示样式
backgroundColor: ‘rgba(0, 0, 0, 0.8)’,
titleColor: ‘#fff’,
bodyColor: ‘#fff’,
borderColor: ‘rgba(255, 99, 132, 1)’,
borderWidth: 1,
padding: 12,
displayColors: false,
},
legend: {
position: ‘top’,
},
title: {
display: true,
text: ‘自定义工具提示’,
},
},
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return ‘¥’ + value.toLocaleString();
},
},
},
},
};
return (
);
};
export default CustomTooltipChart;
6.2 自定义图例点击事件
import React, { useState } from ‘react’;
import { Line } from ‘react-chartjs-2’;
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from ‘chart.js’;
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
const LegendInteractionChart = () => {
const [activeDatasets, setActiveDatasets] = useState([0, 1]);
const data = {
labels: [‘1月’, ‘2月’, ‘3月’, ‘4月’, ‘5月’],
datasets: [
{
label: ‘产品A’,
data: [12000, 19000, 15000, 25000, 22000],
borderColor: ‘rgb(255, 99, 132)’,
backgroundColor: ‘rgba(255, 99, 132, 0.5)’,
tension: 0.4,
},
{
label: ‘产品B’,
data: [8000, 12000, 10000, 18000, 15000],
borderColor: ‘rgb(54, 162, 235)’,
backgroundColor: ‘rgba(54, 162, 235, 0.5)’,
tension: 0.4,
},
],
};
const options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘top’,
onClick: (e, legendItem, legend) => {
const index = legendItem.datasetIndex;
const chart = legend.chart;
// 切换数据集的可见性
const meta = chart.getDatasetMeta(index);
meta.hidden = !meta.hidden;
// 更新状态
setActiveDatasets(prev => {
if (prev.includes(index)) {
return prev.filter(i => i !== index);
} else {
return […prev, index];
}
});
chart.update();
},
labels: {
color: (context) => {
// 根据数据集索引设置颜色
return context.datasetIndex === 0 ? ‘#ff6384’ : ‘#36a2eb’;
},
font: {
weight: ‘bold’,
},
},
},
title: {
display: true,
text: ‘可交互图例(点击切换)’,
},
},
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return ‘¥’ + value.toLocaleString();
},
},
},
},
};
return (
);
};
export default LegendInteractionChart;
6.3 混合图表(Mixed Charts)
import React from ‘react’;
import { Chart } from ‘react-chartjs-2’;
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
BarElement,
Title,
Tooltip,
Legend,
} from ‘chart.js’;
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
BarElement,
Title,
Tooltip,
Legend
);
const MixedChart = () => {
const data = {
labels: [‘1月’, ‘2月’, ‘3月’, ‘4月’, ‘5月’, ‘6月’],
datasets: [
{
type: ‘bar’,
label: ‘柱状图数据’,
data: [12000, 19000, 15000, 25000, 22000, 30000],
backgroundColor: ‘rgba(54, 162, 235, 0.7)’,
borderColor: ‘rgba(54, 162, 235, 1)’,
borderWidth: 1,
},
{
type: ‘line’,
label: ‘折线图数据’,
data: [12000, 19000, 15000, 25000, 22000, 30000],
borderColor: ‘rgb(255, 99, 132)’,
backgroundColor: ‘rgba(255, 99, 132, 0.5)’,
tension: 0.4,
fill: false,
yAxisID: ‘y’,
},
{
type: ‘line’,
label: ‘趋势线’,
data: [10000, 15000, 20000, 25000, 28000, 32000],
borderColor: ‘rgb(75, 192, 192)’,
backgroundColor: ‘rgba(75, 192, 192, 0.3)’,
borderDash: [5, 5],
tension: 0.4,
fill: true,
},
],
};
const options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘top’,
},
title: {
display: true,
text: ‘混合图表(柱状图+折线图)’,
},
},
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return ‘¥’ + value.toLocaleString();
},
},
},
},
};
return (
);
};
export default MixedChart;
第七部分:完整项目示例与部署
7.1 创建完整的仪表板组件
创建
src/components/Dashboard.js
:
import React, { useState, useEffect } from ‘react’;
import { Line, Bar, Pie, Radar } from ‘react-chartjs-2’;
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
BarElement,
ArcElement,
RadialLinearScale,
Filler,
Title,
Tooltip,
Legend,
} from ‘chart.js’;
// 注册所有需要的组件
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
BarElement,
ArcElement,
RadialLinearScale,
Filler,
Title,
Tooltip,
Legend
);
const Dashboard = () => {
const [timeRange, setTimeRange] = useState(‘week’);
const [chartData, setChartData] = useState(null);
// 模拟数据生成
const generateData = (range) => {
const labels = range === ‘week’
? [‘周一’, ‘周二’, ‘周三’, ‘周四’, ‘周五’, ‘周六’, ‘周日’]
: range === ‘month’
? Array.from({ length: 30 }, (_, i) => `${i + 1}日`)
: Array.from({ length: 12 }, (_, i) => `${i + 1}月`);
const values = labels.map(() => Math.floor(Math.random() * 1000) + 500);
return {
labels,
datasets: [
{
label: ‘访问量’,
data: values,
borderColor: ‘rgb(255, 99, 132)’,
backgroundColor: ‘rgba(255, 99, 132, 0.5)’,
tension: 0.4,
},
],
};
};
useEffect(() => {
setChartData(generateData(timeRange));
}, [timeRange]);
const commonOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: ‘top’,
},
},
};
// 饼图数据
const pieData = {
labels: [‘直接访问’, ‘搜索引擎’, ‘社交媒体’, ‘外部链接’, ‘其他’],
datasets: [
{
label: ‘流量来源’,
data: [35, 25, 20, 15, 5],
backgroundColor: [
‘rgba(255, 99, 132, 0.7)’,
‘rgba(54, 162, 235, 0.7)’,
‘rgba(255, 206, 86, 0.7)’,
‘rgba(75, 192, 192, 0.7)’,
‘rgba(153, 102, 255, 0.7)’,
],
},
],
};
// 柱状图数据
const barData = {
labels: [‘产品A’, ‘产品B’, ‘产品C’, ‘产品D’, ‘产品E’],
datasets: [
{
label: ‘销量’,
data: [65, 59, 80, 81, 56],
backgroundColor: ‘rgba(54, 162, 235, 0.7)’,
borderColor: ‘rgba(54, 162, 235, 1)’,
borderWidth: 1,
},
],
};
// 雷达图数据
const radarData = {
labels: [‘速度’, ‘力量’, ‘技巧’, ‘耐力’, ‘敏捷’],
datasets: [
{
label: ‘选手A’,
data: [85, 90, 70, 80, 95],
fill: true,
backgroundColor: ‘rgba(255, 99, 132, 0.2)’,
borderColor: ‘rgb(255, 99, 132)’,
},
{
label: ‘选手B’,
data: [70, 85, 90, 75, 80],
fill: true,
backgroundColor: ‘rgba(54, 162, 235, 0.2)’,
borderColor: ‘rgb(54, 162, 235)’,
},
],
};
return (
数据仪表板
{/* 主图表 */}
访问趋势
{/* 饼图 */}
流量来源
{/* 柱状图 */}
产品销量
{/* 雷达图 */}
能力评估
);
};
export default Dashboard;
7.2 部署优化建议
1. 代码分割与懒加载
import React, { Suspense, lazy } from ‘react’;
// 懒加载图表组件
const LineChart = lazy(() => import(‘./components/charts/LineChart’));
const BarChart = lazy(() => import(‘./components/charts/BarChart’));
function App() {
return (
}>