# 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。