第二章:基础图表开发
2.1 柱状图开发全流程
1. 基础实现
// 柱状图组件
const BarChart = () => {
const chartRef = useRef<HTMLDivElement>(null);
const [data] = useState<number[]>([120, 200, 150, 80, 70]);
useEffect(() => {
const chart = echarts.init(chartRef.current!);
const option: EChartsOption = {
grid: { left: '15%', right: '10%' },
xAxis: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五'],
axisLabel: {
color: '#666',
rotate: 45 // 标签倾斜防重叠
}
},
yAxis: {
type: 'value',
splitLine: { show: true, lineStyle: { type: 'dashed' } }
},
series: [{
name: '销量',
type: 'bar',
data,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 1, color: '#188df0' }
])
},
emphasis: {
itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0,0,0,0.5)' }
}
}]
};
chart.setOption(option);
return () => chart.dispose();
}, [data]);
return <div ref={chartRef} style={{ width: '100%', height: 400 }} />;
};
2. 进阶功能
-
动态数据更新:结合WebSocket实现实时刷新
const [realTimeData, setRealTimeData] = useState<number[]>([]); useEffect(() => { const ws = new WebSocket('wss://api.example.com/sales'); ws.onmessage = (e) => { setRealTimeData(prev => [JSON.parse(e.data), ...prev].slice(0, 5)); }; return () => ws.close(); }, []); -
多系列对比
series: [ { name: '线上', type: 'bar', data: [80, 150, 120, 90, 110] }, { name: '线下', type: 'bar', data: [40, 80, 60, 30, 50] }, { name: '目标线', type: 'line', markLine: { data: [{ yAxis: 100 }], lineStyle: { color: 'red', type: 'dotted' } } } ]
3. 性能优化
-
虚拟滚动:处理万级数据时启用
series: [{ type: 'bar', large: true, largeThreshold: 2000, progressive: 1000, progressiveThreshold: 5000 }] -
防抖更新:防止频繁数据变更导致的重绘
const debouncedSetOption = useCallback(debounce((chart, option) => { chart.setOption(option); }, 300), []);
2.2 折线图高阶应用
1. 多维度展示
series: [
{
name: '温度',
type: 'line',
yAxisIndex: 0,
data: [22, 24, 26, 23, 25],
markArea: {
data: [[{name: '高温', xAxis: '周一'}, {xAxis: '周三'}]] // 区域标注
}
},
{
name: '湿度',
type: 'line',
yAxisIndex: 1,
data: [55, 60, 65, 58, 62],
yAxis: {
type: 'value',
min: 0,
max: 100,
axisLabel: { formatter: '{value}%' }
}
}
]
2. 动画配置
animation: {
duration: 1000, // 动画时长
easing: 'cubicInOut' // 缓动效果
},
animationDelay: (idx) => idx * 100, // 逐个动画延迟
animationEasingUpdate: 'quinticInOut' // 更新动画效果
3. 交互增强
-
数据区域缩放
dataZoom: [{ type: 'slider', start: 0, end: 60, handleSize: '80%' }, { type: 'inside', start: 0, end: 60 }] -
自定义提示框
tooltip: { trigger: 'axis', formatter: (params) => { const date = new Date(params[0].axisValue); return ` <div>${date.toLocaleDateString()}</div> ${params.map(p => ` <div style="color:${p.color}">${p.marker}${p.seriesName}: ${p.value}</div> `).join('')} `; } }
2.3 饼图与地图专项
1. 饼图优化技巧
-
环形图+图例联动
series: [{ type: 'pie', radius: ['55%', '70%'], // 环形效果 label: { position: 'outside', formatter: '{b}\n{c} ({d}%)' }, labelLine: { length: 20, length2: 30 }, data: [ {value: 335, name: '直接访问'}, {value: 310, name: '邮件营销'}, {value: 234, name: '联盟广告'}, {value: 135, name: '视频广告'}, {value: 1548, name: '搜索引擎'} ] }] -
动态占比动画
animationEasing: 'cubicInOut', animationDelay: (idx) => Math.random() * 200
2. 地图开发全流程
-
中国地图基础配置
import 'echarts/map/js/china'; // 引入中国地图数据 series: [{ type: 'map', map: 'china', roam: true, // 允许缩放平移 label: { show: true, color: '#fff' }, itemStyle: { areaColor: '#323c48', borderColor: '#111' }, emphasis: { label: { show: false }, itemStyle: { areaColor: '#2a333d' } }, data: [ {name: '北京', value: 100}, {name: '上海', value: 200}, // 其他省份数据 ] }] -
自定义地理坐标
geo: { map: 'custom', roam: true, itemStyle: { areaColor: '#eee' }, regions: [{ name: '重点区域', itemStyle: { areaColor: 'rgba(255,0,0,0.3)' }, label: { show: false } }] }
2.4 图表联动与组合
1. 多图表联动
// 主图表点击触发副图表更新
const handleMainChartClick = (params: any) => {
setFilterData(params.name);
};
// 在副图表配置中绑定过滤条件
series: [{
data: filteredData.map(item => item.value)
}]
2. 组合图表布局
grid: [
{ left: '5%', top: '10%', height: '30%' }, // 柱状图区域
{ left: '5%', top: '50%', height: '30%' } // 折线图区域
],
3. 响应式适配
useEffect(() => {
const resizeObserver = new ResizeObserver(() => {
chart.resize();
});
resizeObserver.observe(chartRef.current!);
return () => resizeObserver.disconnect();
}, []);
2.5 企业级开发规范
1. 主题定制方案
// 自定义主题文件 theme.js
export default {
color: ['#5470C6', '#91CC75', '#EE6666', '#FAC858', '#73C0DE'],
backgroundColor: 'transparent',
textStyle: { fontFamily: 'PingFang SC, Microsoft YaHei' },
title: { textStyle: { fontWeight: 'bold' } }
};
// 初始化时应用主题
const chart = echarts.init(dom, theme);
2. 异常处理机制
useEffect(() => {
try {
const chart = echarts.init(chartRef.current!);
// ...初始化逻辑
} catch (error) {
console.error('[ECharts Error]', error);
Sentry.captureException(error); // 上报错误监控
}
}, []);
3. 代码规范示例
// 类型定义
interface ChartConfig {
title?: string;
xAxisData: string[];
seriesData: number[];
}
// 组件Props
type BarChartProps = {
config: ChartConfig;
height?: number;
style?: React.CSSProperties;
};
2.6 性能调优实战
1. 大数据量处理方案
| 数据量级 | 优化方案 | 效果 |
|---|---|---|
| 1k-1w | Canvas渲染 | 保持流畅 |
| 1w-10w | 数据采样 + 分段加载 | 首屏加载优化 |
| 10w+ | WebGL渲染 + 虚拟滚动 | 高性能展示 |
2. 内存泄漏预防
// 使用useRef保存实例
const chartRef = useRef<echarts.ECharts | null>(null);
useEffect(() => {
chartRef.current = echarts.init(container);
return () => {
if (chartRef.current) {
chartRef.current.dispose();
chartRef.current = null;
}
};
}, []);
3. Web Worker应用
// worker.ts
self.onmessage = (e) => {
const processedData = heavyDataProcessing(e.data);
self.postMessage(processedData);
};
// 主线程
const worker = new Worker('./worker.ts');
worker.onmessage = (e) => {
setChartData(e.data);
};
2.7 常见问题解决方案
1. 图表不显示排查清单
- 检查容器尺寸是否为0
- 确认数据格式是否正确(数组/对象)
- 验证ECharts初始化时机(DOM加载完成)
- 检查控制台错误信息
2. 中文字体缺失处理
// 在public/index.html添加
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700&display=swap">
// 在CSS中应用
.echarts {
font-family: 'Noto Sans SC', sans-serif !important;
}
3. 移动端适配方案
// 启用响应式布局
const resizeHandler = () => {
if (chart) {
chart.resize();
chart.dispatchAction({ type: 'resize' });
}
};
// 监听页面尺寸变化
useEffect(() => {
window.addEventListener('resize', resizeHandler);
return () => window.removeEventListener('resize', resizeHandler);
}, []);