本文档介绍 ECharts 的高级应用场景,包括服务端渲染、微信小程序、富文本标签等。
通常情况下,ECharts 会在浏览器中动态渲染图表。但在某些特殊场景下,需要在服务端渲染图表并输出到浏览器:
| 渲染方案 | 渲染结果 | 优点 |
|---|---|---|
| 服务端 SVG 渲染 | SVG 字符串 | 体积更小;矢量图不模糊;支持初始动画 |
| 服务端 Canvas 渲染 | 图片 | 适用场景更广泛,对不支持 SVG 的场景可选择 |
使用 5.3.0 及更新版本,推荐使用零依赖的服务端 SVG 字符串渲染方案:
const echarts = require('echarts')
const chart = echarts.init(null, null, {
renderer: 'svg',
ssr: true,
width: 400,
height: 300
})
chart.setOption({
// 配置项
})
const svgStr = chart.renderToSVGString()参数说明:
container 传入 null 或 undefined(服务端不需要容器)ssr: true 开启服务端渲染模式width 和 height 显式指定图表尺寸res.writeHead(200, {
'Content-Type': 'application/xml'
})
res.write(chart.renderToSVGString())
res.end()fs.writeFile('bar.svg', chart.renderToSVGString(), 'utf-8')chart.setOption({
animation: false
})使用 node-canvas 实现(ECharts 5 中 setCanvasCreator 已废弃,需改用 setPlatformAPI):
var echarts = require('echarts')
const { createCanvas } = require('canvas')
echarts.setPlatformAPI({
createCanvas() {
return createCanvas()
}
})
const canvas = createCanvas(800, 600)
const chart = echarts.init(canvas)
chart.setOption({
// 配置项
})
res.writeHead(200, {
'Content-Type': 'image/png'
})
res.write(canvas.toBuffer('image/png'))
res.end()echarts.setPlatformAPI({
createCanvas() {
return createCanvas()
},
loadImage(src, onload, onerror) {
const img = new Image()
img.onload = onload.bind(img)
img.onerror = onerror.bind(img)
img.src = src
return img
}
})服务端渲染无法支持的功能:
解决方案:先用 SVG 做服务端渲染,再用 Canvas 做客户端渲染(Hydration)。
// 客户端渲染时
myChart.setOption({
tooltip: { show: true }, // 开启交互组件
animation: 0 // 关闭初始动画(由服务端 SVG 动画完成)
})echarts-for-weixin 项目提供了小程序组件。
ec-canvas 目录下的 echarts.js 替换为最新版pages 目录下的示例文件作为参考force-use-old-canvas="true" 时,使用新的 Canvas 2d(默认)从 v3.7 开始,ECharts 支持富文本模式,能够:
labelOption = {
formatter: [
'{a|这段文本采用样式a}',
'{b|这段文本采用样式b}这段用默认样式{x|这段用样式x}'
].join('\n'),
color: '#333',
fontSize: 5,
fontFamily: 'Arial',
borderWidth: 3,
backgroundColor: '#984455',
padding: [3, 10, 10, 5],
lineHeight: 20,
rich: {
a: {
color: 'red',
lineHeight: 10
},
b: {
backgroundColor: {
image: 'xxx/xxx.jpg'
},
height: 40
},
x: {
fontSize: 18,
fontFamily: 'Microsoft YaHei',
borderColor: '#449933',
borderRadius: 4
}
}
}option = {
series: [
{
type: 'scatter',
symbolSize: 1,
data: [
{
value: [0, 0],
label: {
show: true,
formatter: [
'Plain text',
'{textBorder|textBorderColor + textBorderWidth}',
'{textShadow|textShadowColor + textShadowBlur}',
'{bg|backgroundColor + borderRadius + padding}',
'{border|borderColor + borderWidth + borderRadius}',
'{shadow|shadowColor + shadowBlur}'
].join('\n'),
backgroundColor: '#eee',
borderColor: '#333',
borderWidth: 2,
borderRadius: 5,
padding: 10,
color: '#000',
fontSize: 14,
lineHeight: 30,
rich: {
textBorder: {
fontSize: 20,
textBorderColor: '#000',
textBorderWidth: 3,
color: '#fff'
},
textShadow: {
fontSize: 16,
textShadowBlur: 5,
textShadowColor: '#000',
color: '#fff'
},
bg: {
backgroundColor: '#339911',
color: '#fff',
borderRadius: 15,
padding: 5
},
border: {
color: '#000',
borderColor: '#449911',
borderWidth: 1,
borderRadius: 3,
padding: 5
},
shadow: {
backgroundColor: '#992233',
padding: 5,
color: '#fff',
shadowBlur: 5,
shadowColor: '#336699'
}
}
}
}
]
}
]
}通过 label.position 和 label.distance 配置:
option = {
series: [
{
type: 'scatter',
label: {
position: 'top', // 支持:'left', 'right', 'top', 'bottom', 'inside' 等
distance: 10
}
}
]
}const labelOption = {
show: true,
rotate: 90,
formatter: '{c} {name|{a}}',
fontSize: 16,
rich: {
name: {}
}
}
option = {
xAxis: [
{
type: 'category',
data: ['2012', '2013', '2014', '2015', '2016']
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: 'Forest',
type: 'bar',
label: labelOption,
data: [320, 332, 301, 334, 390]
}
]
}每个文本片段可以想象成 CSS 中的 inline-block,在文档流中按行放置。
自定义系列(custom series)是一种系列类型,它能让开发者定制渲染逻辑,从而在坐标系中绘制出自定义的图形。
option = {
series: {
type: 'custom',
renderItem: function(params, api) {
// 返回图形元素的定义
return {
type: 'group',
children: [
{
type: 'circle',
shape: {
cx: api.coord([api.value(0), api.value(1)])[0],
cy: api.coord([api.value(0), api.value(1)])[1],
r: 20
},
style: {
fill: 'blue'
}
}
]
}
},
data: [
[10, 20],
[30, 40]
]
}
}dataZoom 组件用于实现数据区域缩放。
option = {
dataZoom: [
{
type: 'inside',
start: 0,
end: 100
}
]
}option = {
dataZoom: [
{
type: 'slider',
show: true,
start: 0,
end: 100
}
]
}option = {
dataZoom: [
{
type: 'inside',
start: 0,
end: 100
},
{
type: 'slider',
start: 0,
end: 100
}
]
}toolbox 组件提供了一些内置的工具按钮。
option = {
toolbox: {
show: true,
feature: {
dataZoom: {
yAxisIndex: 'none'
},
dataView: { readOnly: false },
magicType: { type: ['line', 'bar'] },
restore: {},
saveAsImage: {}
}
}
}timeline 组件用于在多个 option 之间进行切换。
option = {
baseOption: {
timeline: {
data: ['2013', '2014', '2015'],
axisType: 'category'
},
xAxis: {
type: 'category'
},
yAxis: {}
},
options: [
{
series: { type: 'bar', data: [10, 20, 30] }
},
{
series: { type: 'bar', data: [15, 25, 35] }
},
{
series: { type: 'bar', data: [20, 30, 40] }
}
]
}减少打包体积的最有效方式:
// 只引入需要的组件
import * as echarts from 'echarts/core'
import { BarChart, LineChart } from 'echarts/charts'
import {
TitleComponent,
TooltipComponent,
GridComponent
} from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
echarts.use([
TitleComponent,
TooltipComponent,
GridComponent,
BarChart,
LineChart,
CanvasRenderer
])const chart = echarts.init(dom, null, {
useDirtyRect: true // 开启脏矩形渲染
})
option = {
animation: false, // 大数据量时关闭动画
series: [{
type: 'scatter',
large: true, // 开启大数据优化
largeThreshold: 2000, // 触发阈值
progressive: 200, // 渐进式渲染每帧帧数
progressiveThreshold: 3000, // 渐进式渲染阈值
data: largeDataArray
}]
}对于动态追加的数据:
// 初始化时设置基础配置
chart.setOption(baseOption)
// 增量追加数据
function appendData(newData) {
chart.appendData({
seriesIndex: 0,
data: newData
})
}
// 分批加载数据
async function loadLargeData() {
chart.showLoading()
for (let i = 0; i < batches; i++) {
const batchData = await fetchBatch(i)
appendData(batchData)
}
chart.hideLoading()
}// 及时销毁不再使用的实例
function cleanup() {
chart.dispose()
chart = null
}
// 移除事件监听
chart.off('click')
// 清空数据
chart.clear()
// 使用弱引用(高级用法)
const chartRef = new WeakRef(chart)| 场景 | 推荐渲染器 | 原因 |
|---|---|---|
| 大数据量 | Canvas | 性能更好 |
| 移动端 | SVG | 内存占用低 |
| 需要截图导出 | Canvas | 导出更清晰 |
| 动态交互多 | Canvas | 响应更快 |
| 图表数量多 | SVG | 内存更友好 |
// 根据场景选择渲染器
const renderer = dataLength > 10000 ? 'canvas' : 'svg'
const chart = echarts.init(dom, null, { renderer })// 监控渲染性能
const startTime = performance.now()
chart.setOption(option)
console.log(`渲染耗时: ${performance.now() - startTime}ms`)
// 使用 PerformanceObserver
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name.includes('echarts')) {
console.log('ECharts 性能:', entry)
}
}
})
observer.observe({ entryTypes: ['measure'] })// ECharts 没有全局的日志级别 API,控制台警告需按提示修正配置;
// 调试时建议先导出当前配置排查
const option = chart.getOption()
console.log('当前配置:', JSON.stringify(option, null, 2))问题 1:图表不显示
// 排查步骤
function debugChart(dom) {
// 1. 检查容器
console.log('容器尺寸:', dom.offsetWidth, dom.offsetHeight)
if (dom.offsetWidth === 0 || dom.offsetHeight === 0) {
console.error('容器宽高为 0')
}
// 2. 检查实例
const chart = echarts.getInstanceByDom(dom)
console.log('图表实例:', chart)
// 3. 检查配置
if (chart) {
console.log('当前配置:', chart.getOption())
}
}问题 2:数据更新不生效
// 调试数据更新
function debugSetOption(chart, option) {
console.log('旧配置:', chart.getOption())
console.log('新配置:', option)
chart.setOption(option, true) // 使用 notMerge 模式
console.log('更新后配置:', chart.getOption())
}问题 3:事件不触发
// 检查事件绑定
function debugEvents(chart) {
// 列出所有事件监听器
console.log('事件监听器:', chart._events)
// 测试事件触发
chart.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: 0
})
}// 在控制台暴露图表实例
window.myChart = chart
// 然后可以在控制台直接操作
// myChart.setOption({ ... })
// myChart.getOption()
// myChart.getDataURL()option = {
// 显示组件边界(调试用)
grid: {
show: true,
backgroundColor: 'rgba(0, 0, 0, 0.1)',
borderColor: 'red'
},
// 显示数据索引
series: [{
label: {
show: true,
formatter: function(params) {
return `idx:${params.dataIndex}\nval:${params.value}`
}
}
}]
}large 模式emphasis 状态// ECharts 没有全局 error 事件,需自行捕获 setOption 等调用的异常
try {
chart.setOption(option)
} catch (e) {
console.error('配置错误:', e)
chart.setOption(fallbackOption)
}