<p align="center"><a href="https://vue-echarts.dev/"><img alt="Vue ECharts" src="https://raw.githubusercontent.com/ecomfe/vue-echarts/refs/heads/main/demo/public/favicon.svg" width="96"></a></p>
<h1 align="center">Vue ECharts</h1>

<p align="center">Apache ECharts™ 的 Vue.js 组件。</p>
<p align="center"><a href="https://npmjs.com/package/vue-echarts"><img alt="npm 版本" src="https://img.shields.io/npm/v/vue-echarts"></a> <a href="https://codecov.io/gh/ecomfe/vue-echarts"><img alt="测试覆盖率" src="https://img.shields.io/codecov/c/github/ecomfe/vue-echarts"></a> <a href="https://vue-echarts.dev/"><img src="https://img.shields.io/badge/%E6%BC%94%E7%A4%BA%20%C2%BB-20c3aa" alt="查看演示"></a></p>

> 还在使用 Vue 2？可以继续阅读老版本的文档。[前往 →](https://github.com/ecomfe/vue-echarts/blob/7.x/README.zh-Hans.md)

## 安装 & 使用

### npm

```sh
npm install echarts vue-echarts
```

#### 示例

<details open>
<summary><a href="https://stackblitz.com/edit/vue-echarts-8?file=src%2FApp.vue">Demo →</a></summary>

```vue
<template>
  <VChart class="chart" :option="option" />
</template>

<script setup>
import { use } from "echarts/core";
import { CanvasRenderer } from "echarts/renderers";
import { PieChart } from "echarts/charts";
import { TitleComponent, TooltipComponent, LegendComponent } from "echarts/components";
import VChart, { THEME_KEY } from "vue-echarts";
import { ref, provide } from "vue";

use([CanvasRenderer, PieChart, TitleComponent, TooltipComponent, LegendComponent]);

provide(THEME_KEY, "dark");

const option = ref({
  title: {
    text: "Traffic Sources",
    left: "center",
  },
  tooltip: {
    trigger: "item",
    formatter: "{a} <br/>{b} : {c} ({d}%)",
  },
  legend: {
    orient: "vertical",
    left: "left",
    data: ["Direct", "Email", "Ad Networks", "Video Ads", "Search Engines"],
  },
  series: [
    {
      name: "Traffic Sources",
      type: "pie",
      radius: "55%",
      center: ["50%", "60%"],
      data: [
        { value: 335, name: "Direct" },
        { value: 310, name: "Email" },
        { value: 234, name: "Ad Networks" },
        { value: 135, name: "Video Ads" },
        { value: 1548, name: "Search Engines" },
      ],
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: "rgba(0, 0, 0, 0.5)",
        },
      },
    },
  ],
});
</script>

<style scoped>
.chart {
  height: 400px;
}
</style>
```

</details>

#### 按需导入&nbsp;<sup><a href="#按需导入"><img src="https://img.shields.io/badge/%E6%8E%A8%E8%8D%90-10B981" alt="推荐" align="middle" height="16"></a></sup>

为了减小打包体积，我们建议手动从 ECharts 中导入所需的组件和图表。我们提供了一个[导入代码生成器](https://vue-echarts.dev/#codegen)，可以帮你自动生成导入语句。只需将你的 `option` 代码粘贴进去，工具就会为你生成精确的导入代码。

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="assets/codegen-dark.webp">
  <source media="(prefers-color-scheme: light)" srcset="assets/codegen-light.webp">
  <img alt="一个用于生成 ECharts 导入代码的弹窗。左侧面板展示图表的 JSON 配置，右侧面板展示对应的 TypeScript 导入语句。" src="assets/codegen-light.webp">
</picture>

[试一试 →](https://vue-echarts.dev/#codegen)

但如果你实在需要全量引入 ECharts 从而无需手动引入模块，只需要在代码中添加：

```js
import "echarts";
```

#### 样式

在浏览器中导入 Vue ECharts 时，它会将基础样式注入全局 document，通常无需另行引入 CSS。若组件位于 shadow root 或另一个 document，请在相应的样式作用域中引入 `vue-echarts/style.css`；旧版浏览器所需的 CSP 回退方式请参见 [CSP](#csp-style-src-或-style-src-elem)。

### 服务端渲染

`VChart` 可以在 Vue SSR 框架中渲染和水合。服务端只输出图表容器；组件在浏览器中挂载后才会初始化 ECharts。`init-options` 中 ECharts 底层的 `ssr` 字段不会为 `VChart` 启用服务端图表渲染。

### CDN

用如下方式在 HTML 中插入 `<script>` 标签，并且通过 `window.VueECharts` 来访问组件接口：

<details>
<summary><a href="https://stackblitz.com/edit/vue-echarts-8-global?file=index.html">Demo →</a></summary>

<!-- scripts:start -->

```html
<script src="https://cdn.jsdelivr.net/npm/echarts@6.1.0"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@3.5.41"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-echarts@8.3.0"></script>
```

<!-- scripts:end -->

```js
const app = Vue.createApp(...)

// 全局注册组件（也可以使用局部注册）
app.component('VChart', VueECharts)
```

</details>

可以在[这里](https://github.com/ecomfe/vue-echarts/tree/main/demo)查看更多例子。

### Props

- `init-options: object`

  初始化附加参数。请参考 `echarts.init` 的 `opts` 参数。[前往 →](https://echarts.apache.org/zh/api.html#echarts.init)

  Inject 键名：`INIT_OPTIONS_KEY`。

- `theme: string | object`

  要应用的主题。请参考 `echarts.init` 的 `theme` 参数。[前往 →](https://echarts.apache.org/zh/api.html#echarts.init)

  ECharts 在切换主题时会重建模型。Vue ECharts 会重新应用最新的自动更新配置，但未受控的交互状态（如图例选择、数据缩放）可能重置。需要跨重建保留的状态应维护在 `option` 中。

  传入空字符串可以在覆盖注入主题的同时使用 ECharts 默认主题。

  Inject 键名：`THEME_KEY`。

- `option: object`

  ECharts 的万能接口。修改这个 prop 会触发 ECharts 实例的 `setOption` 方法。查看[详情 →](https://echarts.apache.org/zh/option.html)
  暂时移除该 prop 会暂停自动更新，之后的主题变更也不会让图表回退到初始 option。

  #### 智能更新

  自动 option、theme 和插槽变化会在 Vue 更新后合并处理。`clear()` 立即生效并取消已经排队的自动更新，后续变化仍可重新填充图表。

  响应式更新描述完整配置。Vue ECharts 会在合并能够正确应用配置时保留现有模型，并在必要时通过重建清除旧配置。重建可能重置图例选择、数据缩放等交互状态。

  - 如果提供了 `update-options`（或通过 inject 注入），Vue ECharts 会直接把它传给 `setOption`，不会执行智能计划。移除它之后，首次源 option 智能更新会重建一次，以建立可靠的结构基线。
  - option 或主题提交失败会使结构基线失效。下一次智能更新会重建，避免依赖可能只完成了一部分的更新。
  - 手动调用 `setOption`（仅当 `manual-update` 为 `true` 时可用）与原生 ECharts 保持一致，只使用本次调用传入的参数，重新初始化后不会保留这些调用的效果。
  - 如果更新中有 graphic 元素使用 `$action`，`graphic` 会保留普通合并，让命令能够作用于现有元素树；无关组件的安全删除仍会使用 `replaceMerge`。需要完整重建的变更无法与这些命令一起应用。若需要完整快照语义，请直接描述更新后的 graphic 元素树，不使用 `$action` 命令。
  - 其他情况下，Vue ECharts 会分析差异：组件删除、重排及匿名组件内部的属性删除会在 `replaceMerge` 能还原目标顺序时使用它；已按 ID 匹配的组件内部属性删除、`replaceMerge` 无法还原的身份顺序变化、新增或缩短的非组件数组、首次 ARIA 配置及其他高风险变更会退回 `notMerge: true`。

- `update-options: object`

  图表更新的配置项。一旦提供（或通过 inject 注入），Vue ECharts 会直接把它传给 `setOption` 并跳过智能更新。请参考 `echartsInstance.setOption` 的 `opts` 参数。[前往 →](https://echarts.apache.org/zh/api.html#echartsInstance.setOption)

  Inject 键名：`UPDATE_OPTIONS_KEY`。

- `group: string`

  图表的分组，用于[联动](https://echarts.apache.org/zh/api.html#echarts.connect)。请参考 `echartsInstance.group`。[前往 →](https://echarts.apache.org/zh/api.html#echartsInstance.group)

- `autoresize: boolean | { throttle?: number, onResize?: () => void }`（默认值：`false`）

  图表渲染容器尺寸变化时是否自动调整图表大小。也可以传入一个选项对象来指定自定义的节流延迟和尺寸变化时的额外回调函数。零尺寸容器不会触发重绘；从零尺寸恢复时同样遵循所配置的节流延迟。

- `loading: boolean`（默认值：`false`）

  图表是否处于加载状态。

- `loading-type: string`

  已注册的加载动画名称。该值会作为 `echartsInstance.showLoading` 的第一个参数；省略时使用默认加载动画。

- `loading-options: object`

  加载动画配置项。默认动画的字段具有明确类型，自定义动画所需的其它字段也会转发给 `echartsInstance.showLoading`。请参考其 `opts` 参数。[前往 →](https://echarts.apache.org/zh/api.html#echartsInstance.showLoading)

  Inject 键名：`LOADING_OPTIONS_KEY`。

- `manual-update: boolean`（默认值 `false`）

  适用于性能敏感的场景（例如 `option` 很大或更新频繁）。设为 `true` 时，`option` 只参与首次渲染，此后 Vue 不会再深度观察它；后续的 prop 变更不会触发图表更新，需要你通过模板 `ref` 手动调用 `setOption`。组件负责的首次渲染仍会使用 `update-options`，后续手动调用则只使用本次调用传入的参数。如果 `autoresize` 推迟了首次渲染，而你先成功调用了 `setOption`，则以这次手动调用为准。如果图表因为修改 `init-options`、切换 `manual-update` 或重新挂载而被重新初始化，之前通过 `setOption` 写入的状态会丢失，并重新使用当前的 `option` 值渲染。

#### TypeScript

组件专属的 prop 类型可直接从包入口导入：

```ts
import type { AutoResize, LoadingOptions } from "vue-echarts";
```

模板 ref 可直接使用组件类型：

```ts
import VChart from "vue-echarts";
import { ref } from "vue";

const chart = ref<InstanceType<typeof VChart> | null>(null);
```

Vue 3.5 的 `useTemplateRef` 可以自动推导此类型。

### 事件

可以使用 Vue 的 `v-on` 指令绑定事件。

```vue
<template>
  <VChart :option="option" @highlight="handleHighlight" />
</template>
```

> [!NOTE]
> ECharts 和 ZRender 事件仅支持 `.once` 修饰符；其它修饰符属于 DOM 事件。使用 `native:` 前缀的监听器支持 Vue 常规的 DOM 事件修饰符。

Vue ECharts 支持如下事件：

- `highlight` [→](https://echarts.apache.org/zh/api.html#events.highlight)
- `downplay` [→](https://echarts.apache.org/zh/api.html#events.downplay)
- `selectchanged` [→](https://echarts.apache.org/zh/api.html#events.selectchanged)
- `legendselectchanged` [→](https://echarts.apache.org/zh/api.html#events.legendselectchanged)
- `legendselected` [→](https://echarts.apache.org/zh/api.html#events.legendselected)
- `legendunselected` [→](https://echarts.apache.org/zh/api.html#events.legendunselected)
- `legendselectall` [→](https://echarts.apache.org/zh/api.html#events.legendselectall)
- `legendinverseselect` [→](https://echarts.apache.org/zh/api.html#events.legendinverseselect)
- `legendscroll` [→](https://echarts.apache.org/zh/api.html#events.legendscroll)
- `datazoom` [→](https://echarts.apache.org/zh/api.html#events.datazoom)
- `datarangeselected` [→](https://echarts.apache.org/zh/api.html#events.datarangeselected)
- `graphroam` [→](https://echarts.apache.org/zh/api.html#events.graphroam)
- `georoam` [→](https://echarts.apache.org/zh/api.html#events.georoam)
- `treeroam` [→](https://echarts.apache.org/zh/api.html#events.treeroam)
- `sankeyroam` [→](https://echarts.apache.org/zh/api.html#events.sankeyroam)
- `focusnodeadjacency`、`unfocusnodeadjacency`（旧版图邻接关系聚焦操作事件）
- `dragnode` [→](https://echarts.apache.org/zh/api.html#events.dragnode)
- `treeexpandandcollapse` [→](https://echarts.apache.org/zh/api.html#events.treeexpandandcollapse)
- `timelinechanged` [→](https://echarts.apache.org/zh/api.html#events.timelinechanged)
- `timelineplaychanged` [→](https://echarts.apache.org/zh/api.html#events.timelineplaychanged)
- `restore` [→](https://echarts.apache.org/zh/api.html#events.restore)
- `dataviewchanged` [→](https://echarts.apache.org/zh/api.html#events.dataviewchanged)
- `magictypechanged` [→](https://echarts.apache.org/zh/api.html#events.magictypechanged)
- `geoselectchanged` [→](https://echarts.apache.org/zh/api.html#events.geoselectchanged)
- `geoselected` [→](https://echarts.apache.org/zh/api.html#events.geoselected)
- `geounselected` [→](https://echarts.apache.org/zh/api.html#events.geounselected)
- `axisbreakchanged` [→](https://echarts.apache.org/zh/api.html#events.axisbreakchanged)
- `axisareaselected` [→](https://echarts.apache.org/zh/api.html#events.axisareaselected)
- `brush` [→](https://echarts.apache.org/zh/api.html#events.brush)
- `brushend` [→](https://echarts.apache.org/zh/api.html#events.brushEnd)
- `brushselected` [→](https://echarts.apache.org/zh/api.html#events.brushselected)
- `showtip` [→](https://echarts.apache.org/zh/api.html#action.tooltip.showTip)
- `hidetip` [→](https://echarts.apache.org/zh/api.html#action.tooltip.hideTip)
- `updateaxispointer` [→](https://echarts.apache.org/zh/api.html#action.axisPointer.updateAxisPointer)
- `globalcursortaken` [→](https://echarts.apache.org/zh/api.html#events.globalcursortaken)
- `updated`（ECharts 更新完成后触发）
- `rendered` [→](https://echarts.apache.org/zh/api.html#events.rendered)
- `finished` [→](https://echarts.apache.org/zh/api.html#events.finished)
- 鼠标事件
  - `click` [→](https://echarts.apache.org/zh/api.html#events.Mouse%20events.click)
  - `dblclick` [→](https://echarts.apache.org/zh/api.html#events.Mouse%20events.dblclick)
  - `mouseover` [→](https://echarts.apache.org/zh/api.html#events.Mouse%20events.mouseover)
  - `mouseout` [→](https://echarts.apache.org/zh/api.html#events.Mouse%20events.mouseout)
  - `mousemove` [→](https://echarts.apache.org/zh/api.html#events.Mouse%20events.mousemove)
  - `mousedown` [→](https://echarts.apache.org/zh/api.html#events.Mouse%20events.mousedown)
  - `mouseup` [→](https://echarts.apache.org/zh/api.html#events.Mouse%20events.mouseup)
  - `globalout` [→](https://echarts.apache.org/zh/api.html#events.Mouse%20events.globalout)
  - `contextmenu` [→](https://echarts.apache.org/zh/api.html#events.Mouse%20events.contextmenu)
- ZRender 事件
  - `zr:click`
  - `zr:dblclick`
  - `zr:mouseout`
  - `zr:mouseover`
  - `zr:mouseup`
  - `zr:mousedown`
  - `zr:mousemove`
  - `zr:contextmenu`
  - `zr:globalout`
  - `zr:mousewheel`
  - `zr:drag`
  - `zr:dragstart`
  - `zr:dragend`
  - `zr:dragenter`
  - `zr:dragleave`
  - `zr:dragover`
  - `zr:drop`

更多事件说明可参考 [ECharts 官方事件文档 →](https://echarts.apache.org/zh/api.html#events)

#### 原生 DOM 事件

由于 Vue ECharts 默认将事件绑定到 ECharts 实例，因此在使用原生 DOM 事件时需要做一些特殊处理。你需要在事件名称前加上 `native:` 前缀来绑定原生 DOM 事件。

```vue
<template>
  <VChart @native:click="handleClick" />
</template>
```

如需监听大小写敏感的自定义事件，请在 `native:` 后写出准确的事件名，例如 `@native:ChartReady`。

通过 attrs 传入的事件处理器默认是响应式的。更新 `onClick`、`onZr:*` 或 `onNative:*` 后会自动生效。
多词事件处理器支持自然的驼峰写法，例如 `onDataZoom`、`onBrushEnd` 和 `onZr:mouseMove`；原有的 `onDatazoom`、`onBrushend` 和 `onZr:mousemove` 写法仍然可用。

### Provide / Inject

Vue ECharts 为 `theme`、`init-options`、`update-options` 和 `loading-options` 提供了 provide/inject API，以通过上下文配置选项。例如：可以通过如下方式来使用 provide API 为 `theme` 提供上下文配置：

显式传入的 prop 优先于对应的注入值。
上下文值暂不可用时，响应式 provider 可以返回 `null` 或 `undefined`。

<details>
<summary>组合式 API</summary>

```js
import { THEME_KEY } from "vue-echarts";
import { provide } from "vue";

provide(THEME_KEY, "dark");

// 或者 provide 一个 ref
const theme = ref("dark");
provide(THEME_KEY, theme);

// 也支持 getter
provide(THEME_KEY, () => theme.value);
```

</details>

<details>
<summary>选项式 API</summary>

静态值：

```js
import { THEME_KEY } from "vue-echarts";

export default {
  provide: {
    [THEME_KEY]: "dark",
  },
};
```

响应式值：

```js
import { THEME_KEY } from "vue-echarts";
import { computed } from "vue";

export default {
  data() {
    return {
      theme: "dark",
    };
  },
  provide() {
    return {
      [THEME_KEY]: computed(() => this.theme),
    };
  },
};
```

</details>

### 属性

- `chart: ECharts | undefined`

  当前底层 ECharts 实例。此属性只读；组件重新初始化图表时它会随之更新，销毁后变为 `undefined`。对于已支持的操作，请优先使用下方方法。直接修改实例 option 不会被智能更新器追踪；需要命令式调用 `setOption` 时请使用 `manual-update`。

- `root: HTMLElement | undefined`

  组件只读的 `<x-vue-echarts>` 根元素，挂载后可用。

### 方法

- `setOption` [→](https://echarts.apache.org/zh/api.html#echartsInstance.setOption)
- `getWidth` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getWidth)
- `getHeight` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getHeight)
- `getDom` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getDom)
- `getZr` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getZr)
- `getId` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getId)
- `getOption` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getOption)
- `isSSR` [→](https://echarts.apache.org/zh/api.html#echartsInstance.isSSR)
- `getDevicePixelRatio` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getDevicePixelRatio)
- `resize` [→](https://echarts.apache.org/zh/api.html#echartsInstance.resize)
- `makeActionFromEvent` [→](https://echarts.apache.org/zh/api.html#echartsInstance.makeActionFromEvent)
- `dispatchAction` [→](https://echarts.apache.org/zh/api.html#echartsInstance.dispatchAction)
- `updateLabelLayout` [→](https://echarts.apache.org/zh/api.html#echartsInstance.updateLabelLayout)
- `convertToPixel` [→](https://echarts.apache.org/zh/api.html#echartsInstance.convertToPixel)
- `convertToLayout` [→](https://echarts.apache.org/zh/api.html#echartsInstance.convertToLayout)
- `convertFromPixel` [→](https://echarts.apache.org/zh/api.html#echartsInstance.convertFromPixel)
- `containPixel` [→](https://echarts.apache.org/zh/api.html#echartsInstance.containPixel)
- `getVisual` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getVisual)
- `renderToCanvas` [→](https://echarts.apache.org/zh/api.html#echartsInstance.renderToCanvas)
- `renderToSVGString` [→](https://echarts.apache.org/zh/api.html#echartsInstance.renderToSVGString)
- `getSvgDataURL` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getSvgDataURL)
- `getDataURL` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getDataURL)
- `getConnectedDataURL` [→](https://echarts.apache.org/zh/api.html#echartsInstance.getConnectedDataURL)
- `appendData` [→](https://echarts.apache.org/zh/api.html#echartsInstance.appendData)
- `clear` [→](https://echarts.apache.org/zh/api.html#echartsInstance.clear)
- `isDisposed` [→](https://echarts.apache.org/zh/api.html#echartsInstance.isDisposed)
- `dispose` [→](https://echarts.apache.org/zh/api.html#echartsInstance.dispose)

`dispose` 会终止当前组件实例。需要创建新图表时，请重新挂载组件。

> [!NOTE]
> 如下 ECharts 实例方法没有被暴露，因为它们的功能已经通过组件 [prop](#props) 提供了：
>
> - [`showLoading`](https://echarts.apache.org/zh/api.html#echartsInstance.showLoading) / [`hideLoading`](https://echarts.apache.org/zh/api.html#echartsInstance.hideLoading)：请使用 `loading`、`loading-type` 和 `loading-options` prop。
> - [`setTheme`](https://echarts.apache.org/zh/api.html#echartsInstance.setTheme)：请使用 `theme` prop。

### 插槽

Vue ECharts 当前支持三类插槽：

- 回调类插槽：用于 [`tooltip.formatter`](https://echarts.apache.org/zh/option.html#tooltip.formatter)。
- 回调类插槽：用于 [`toolbox.feature.dataView.optionToContent`](https://echarts.apache.org/zh/option.html#toolbox.feature.dataView.optionToContent)。
- 可选 `#graphic` 插槽（需引入 `vue-echarts/graphic`）：通过 `G*` 组件声明式构建 [`option.graphic`](https://echarts.apache.org/zh/option.html#graphic)。

#### 回调插槽命名约定（`tooltip*` / `dataView*`）

下面的命名规则只适用于回调类插槽，`graphic` 插槽固定使用 `#graphic`。

- 插槽名称以 `tooltip`/`dataView` 开头，后面跟随用连字符分隔的路径片段，用于定位目标。
- 当 `tooltip` 或 `toolbox` 是数组时，紧跟在插槽前缀后的数字表示组件索引；其余片段仍用于定位所属 option。
- 每个非空路径片段对应 `option` 对象的属性名或数组索引（数组索引使用数字形式）。
- JavaScript 的保留路径片段 `__proto__` 会被拒绝。
- 拼接后的插槽名称直接映射到要覆盖的嵌套回调函数。

**示例映射**：

- `tooltip` → `option.tooltip.formatter`
- `tooltip-0` → `option.tooltip[0].formatter`
- `tooltip-baseOption` → `option.baseOption.tooltip.formatter`
- `tooltip-xAxis-1` → `option.xAxis[1].tooltip.formatter`
- `tooltip-series-2-data-4` → `option.series[2].data[4].tooltip.formatter`
- `dataView` → `option.toolbox.feature.dataView.optionToContent`
- `dataView-1` → `option.toolbox[1].feature.dataView.optionToContent`
- `dataView-media-1-option` → `option.media[1].option.toolbox.feature.dataView.optionToContent`

插槽的 props 对象对应回调函数的第一个参数。

<details>
<summary>用法示例</summary>

```vue
<template>
  <VChart :option="chartOptions">
    <!-- 全局 `tooltip.formatter` -->
    <template #tooltip="params">
      <div v-for="(param, i) in params" :key="i">
        <span v-html="param.marker" />
        <span>{{ param.seriesName }}</span>
        <span>{{ param.value[0] }}</span>
      </div>
    </template>

    <!-- x轴 tooltip -->
    <template #tooltip-xAxis="params">
      <div>X轴: {{ params.value }}</div>
    </template>

    <!-- 数据视图内容 -->
    <template #dataView="option">
      <table>
        <thead>
          <tr>
            <th v-for="(t, i) in option.dataset[0].source[0]" :key="i">
              {{ t }}
            </th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="(row, i) in option.dataset[0].source.slice(1)" :key="i">
            <th>{{ row[0] }}</th>
            <td v-for="(v, i) in row.slice(1)" :key="i">{{ v }}</td>
          </tr>
        </tbody>
      </table>
    </template>
  </VChart>
</template>
```

[示例 →](https://vue-echarts.dev/#line)

</details>

> [!NOTE]
> 插槽会优先于 `props.option` 中对应的回调函数。
> 移除回调插槽时会显式清除注入的函数，不会重建图表。
> 在 `manual-update` 模式下新增或移除回调插槽后，需要调用 `chartRef.setOption(...)` 提交最新的插槽集合。

#### Graphic 插槽&nbsp;<sup><a href="#插槽"><img src="https://img.shields.io/badge/%E6%96%B0%E5%8A%9F%E8%83%BD-A855F7" alt="新功能" align="middle" height="16"></a></sup>

```ts
import { GGroup, GRect, GText } from "vue-echarts/graphic";
```

可用组件：

- `GGroup`
- `GRect`
- `GCircle`
- `GEllipse`
- `GText`
- `GLine`
- `GPolyline`
- `GPolygon`
- `GImage`
- `GSector`
- `GRing`
- `GArc`
- `GBezierCurve`

更多细节可参考 [ECharts `option.graphic` →](https://echarts.apache.org/zh/option.html#graphic)

> [!NOTE]
>
> - graphic 元素事件额外支持 `dblclick`、`contextmenu`。
> - 事件支持 `.once` 修饰符。
> - graphic 元素事件监听器返回 `true` 时会停止冒泡。
> - 路径组件支持通过 `auto-batch` 启用 ZRender 的 Canvas 路径批处理。
> - 仅使用 graphic 插槽时可以省略 `option` prop。
> - `#graphic` 会覆盖 `option.graphic`。`manual-update` 模式下需调用 `chartRef.setOption(...)` 提交变更。
> - 包装组件和 Fragment 中的图形遵循实际渲染顺序。可合并的属性变化只更新变更节点，保留未变更元素及其正在运行的动画。删除字段、改变类型或树结构时，会重建 graphic 组件。
> - 仅 graphic 变化时，会在安全的情况下省略无关的源配置。显式指定 `notMerge` 或针对其他组件的 `replaceMerge` 时，仍会提交完整源配置。

<details>
<summary>用法示例</summary>

```vue
<script setup lang="ts">
import { ref } from "vue";
import type { ElementEvent } from "echarts/core";

const option = {
  xAxis: { type: "category", data: ["Mon", "Tue", "Wed"] },
  yAxis: { type: "value" },
  series: [{ type: "line", data: [120, 200, 150] }],
};

const overlay = ref({ x: 84, y: 22 });

function onDrag(event: ElementEvent) {
  overlay.value.x = event.offsetX - 44;
  overlay.value.y = event.offsetY - 14;
}
</script>

<template>
  <VChart :option="option">
    <template #graphic>
      <GGroup id="drag-handle" :x="overlay.x" :y="overlay.y">
        <GRect :width="88" :height="28" :r="6" fill="#5470c6" draggable @drag="onDrag" />
        <GText
          :x="10"
          :y="8"
          :text="`x: ${Math.round(overlay.x)} y: ${Math.round(overlay.y)}`"
          fill="#fff"
        />
      </GGroup>
    </template>
  </VChart>
</template>
```

</details>

### 静态方法

静态方法请直接通过 [`echarts` 本身](https://echarts.apache.org/zh/api.html#echarts)进行调用。

## CSP: `style-src` 或 `style-src-elem`

Vue ECharts 会在模块执行时将基础样式注入全局 document。shadow root 和其它 document 不会获得这些样式；需要时请在每个目标样式作用域中引入 `vue-echarts/style.css`。

如果你执行严格的 CSP 策略来防止内联 `<style>` 注入，**并且**需要兼容不支持 [CSSStyleSheet() 构造函数](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet#browser_compatibility) 的浏览器，则需要手动引入 `vue-echarts/style.css`。

## 迁移到 v8

> [!NOTE]
> 请确保同时查阅 [ECharts 6 的升级指南](https://echarts.apache.org/handbook/zh/basics/release-note/v6-upgrade-guide/)。

`vue-echarts@8` 引入了以下破坏性变更：

- **Vue 2 支持已移除：** 如果你仍需要继续使用 Vue 2，请使用 [`vue-echarts@7`](https://github.com/ecomfe/vue-echarts/tree/7.x)。

- **浏览器兼容性变更：** 我们不再为不支持原生 [`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#browser_compatibility) 的浏览器提供兼容性支持。如果你需要支持旧版浏览器，必须自行将代码转译为 ES5。

- **CSP 入口点已移除：** 入口点 `vue-echarts/csp` 已被移除。请使用 `vue-echarts` 替代。如果你执行严格的 CSP 策略来防止内联 `<style>` 注入，**并且**需要兼容不支持 [`CSSStyleSheet()` 构造函数](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/CSSStyleSheet#browser_compatibility) 的浏览器，则需要手动引入 `vue-echarts/style.css`。

## 本地开发

```sh
pnpm i
pnpm dev
```

打开 `http://localhost:5173` 来查看 demo。

更多测试与 CI 说明请参见 [`tests/TESTING.md`](tests/TESTING.md)。

## 声明

The Apache Software Foundation [Apache ECharts, ECharts](https://echarts.apache.org/), Apache, the Apache feather, and the Apache ECharts project logo are either registered trademarks or trademarks of the [Apache Software Foundation](https://www.apache.org/).
