# ECharts 集成
`markstream-vue` 支持通过自定义组件渲染 ECharts 图表。本文介绍三种推荐方案,从最简单到最高级。
## 为什么在 markstream-vue 中使用 ECharts?
ECharts 是一个强大的图表库,与 `markstream-vue` 的流式架构天然契合。你可以:
- 直接在 Markdown 代码块中编写图表配置
- 流式更新图表(适合 AI 生成的内容)
- 复用现有的 ECharts 主题和配置
- 导出图表为图片
---
## 方案一:自定义 CodeBlock 组件(推荐)
此方案将 ECharts 作为一种特殊的代码块语言,类似于 Mermaid 的处理方式。这是对用户最友好的方式,也符合现有架构模式。
### 1.1 创建 ECharts 组件
创建 `EChartsBlockNode.vue`:
```vue
```
### 1.2 注册自定义组件
```ts
// main.ts 或组件入口文件
import { setCustomComponents } from 'markstream-vue'
import EChartsBlockNode from './components/EChartsBlockNode.vue'
setCustomComponents({
code_block: EChartsBlockNode
})
```
### 1.3 在 Markdown 中使用
现在可以直接在 Markdown 中编写 ECharts 图表:
```markdown
\```echarts
{
"title": { "text": "销售数据" },
"tooltip": {},
"xAxis": {
"type": "category",
"data": ["周一", "周二", "周三", "周四", "周五"]
},
"yAxis": { "type": "value" },
"series": [{
"type": "bar",
"data": [120, 200, 150, 80, 70]
}]
}
\```
```
### 1.4 安装 ECharts
```bash
pnpm add echarts
# 或
npm install echarts
```
### 优缺点
**优点:**
- 熟悉的代码块语法
- 遵循现有的 Mermaid 模式
- 支持流式更新
- 易于实现
**缺点:**
- 会覆盖所有 `code_block` 的渲染(需要处理非 echarts 代码块)
- 需要 JSON 解析(不支持 JS 表达式)
---
## 方案二:自定义 HTML 标签
使用自定义 HTML 类标签来渲染 ECharts 图表,与常规代码块分离更清晰。
### 2.1 配置自定义 HTML 标签
```ts
// 解析器配置(或在 MarkdownRender 上直接传 `custom-html-tags`)
import { getMarkdown } from 'markstream-vue'
const md = getMarkdown('echarts', {
customHtmlTags: ['echarts', 'chart'],
})
```
### 2.2 注册组件
```ts
import { setCustomComponents } from 'markstream-vue'
import EChartsComponent from './components/EChartsComponent.vue'
setCustomComponents({
echarts: EChartsComponent,
chart: EChartsComponent
})
```
### 2.3 创建组件
```vue
```
### 2.4 在 Markdown 中使用
```markdown
{
"series": [{
"type": "pie",
"data": [
{ "value": 335, "name": "直接访问" },
{ "value": 310, "name": "邮件营销" },
{ "value": 234, "name": "联盟广告" }
]
}]
}
```
### 优缺点
**优点:**
- 类型分离清晰
- 支持自定义属性(宽度、高度)
- 不干扰代码块
**缺点:**
- 需要 parser 配置
- 语法不够熟悉
- 不是标准 Markdown
---
## 方案三:VmrContainer 集成
在自定义容器中使用 ECharts(类似 admonitions),适合带图表说明的文档。
### 3.1 注册容器组件
```ts
import { setCustomComponents } from 'markstream-vue'
import EChartsContainerNode from './components/EChartsContainerNode.vue'
setCustomComponents({
vmr_container: EChartsContainerNode
})
```
### 3.2 创建容器组件
```vue
```
### 3.3 在 Markdown 中使用
```markdown
:::echarts
{
"title": { "text": "流量来源" },
"series": [{
"type": "pie",
"radius": "50%",
"data": [
{ "value": 1048, "name": "搜索引擎" },
{ "value": 735, "name": "直接访问" },
{ "value": 580, "name": "邮件营销" }
]
}]
}
:::
```
### 优缺点
**优点:**
- 与现有容器系统一致
- 支持图表周围添加额外内容
- 适合文档场景
**缺点:**
- 配置较复杂
- 需要解析子节点
---
## SSR 注意事项
ECharts 需要 DOM 环境,不支持服务端渲染。请使用以下策略之一:
### Nuxt 3 中使用
```vue
```
### SSR 带降级
```vue
加载图表中...
```
### 组件内检查 window
```ts
import { computed } from 'vue'
const canRender = computed(() => typeof window !== 'undefined' && isECharts.value)
```
---
## 高级功能
### 使用 ResizeObserver 实现响应式图表
```ts
import { useResizeObserver } from '@vueuse/core'
useResizeObserver(chartRef, (entries) => {
if (chartInstance && entries[0]) {
const { width, height } = entries[0].contentRect
if (width > 0 && height > 0) {
chartInstance.resize()
}
}
})
```
### 导出为图片
```ts
function exportChart() {
if (!chartInstance)
return
const url = chartInstance.getDataURL({
type: 'png',
pixelRatio: 2,
backgroundColor: props.isDark ? '#1a1a1a' : '#fff'
})
const link = document.createElement('a')
link.href = url
link.download = 'chart.png'
link.click()
}
```
### 使用 ECharts 主题切换
```ts
import dark from 'echarts/theme/dark'
import { computed } from 'vue'
const theme = computed(() => props.isDark ? 'dark' : undefined)
chartInstance = echarts.init(chartRef.value, theme.value)
```
---
## 应该选择哪种方案?
| 方案 | 适用场景 | 复杂度 |
|------|----------|--------|
| **CodeBlock** | 大多数用户,简单集成 | ⭐ |
| **自定义标签** | 高级用户,多种图表类型 | ⭐⭐ |
| **VmrContainer** | 文档场景,带样式容器 | ⭐⭐⭐ |
**推荐:** 从 **方案一(CodeBlock)** 开始。这是最直接的方式,也符合用户与 Mermaid 图表交互的方式。
---
## 完整示例
使用方案一的完整最小设置:
```ts
import MarkdownRender, { setCustomComponents } from 'markstream-vue'
// main.ts
import { createApp } from 'vue'
import EChartsBlockNode from './EChartsBlockNode.vue'
import 'markstream-vue/index.css'
import 'echarts'
// 注册自定义组件
setCustomComponents({
code_block: EChartsBlockNode
})
const app = createApp({
components: { MarkdownRender },
template: `
`,
data() {
return {
markdown: `
\`\`\`echarts
{
"xAxis": { "type": "category", "data": ["A", "B", "C"] },
"yAxis": { "type": "value" },
"series": [{ "type": "bar", "data": [10, 20, 30] }]
}
\`\`\``
}
}
})
app.mount('#app')
```
---
## 故障排除
1. **图表未渲染** — 检查浏览器控制台的 JSON 解析错误。确保代码块内容是有效的 JSON。
2. **容器空白** — 验证 ECharts 已安装且 DOM 元素有尺寸。设置明确的宽高样式。
3. **主题未切换** — 切换主题时需要重新创建 ECharts 实例。使用 `watch(() => props.isDark)` 来销毁并重新初始化。
4. **SSR 错误** — 在 Nuxt 中使用 `` 包裹组件,或在初始化前检查 `typeof window !== 'undefined'`。
5. **内存泄漏** — 始终在 `onBeforeUnmount` 中调用 `chartInstance.dispose()`。
仍有问题?请查阅 [ECharts 官方文档](https://echarts.apache.org/zh/index.html)或在 GitHub 上提 issue。