在ECharts的tooltip中使用Vue组件 | 用JS代码的方式调用Vue组件

ECharts的tooltip中的formatter函数提供了富文本或函数回调的方式来允许开发者自定义tooltip中的内容。

接下来通过探索tooltip.formatter的用法来一步步实现在tooltip中使用Vue组件,举一反三同样的方式也可以实现类似Element Plus中的ElMessage.success()使用调用函数的方式来调用显示Vue组件。

1. 简单的tooltip内容

ECharts的tooltip.formatter提供了字符串模板,通过配置模板可以很容易实现一些简单的tooltip内容定制。

同时formatter还接受一个回调函数,函数的返回值可以是string,HTMLElement,HTMLElement[], 字符串可以是模板也可以是html或者二者皆有。

一个使用模板字符串的例子: 直接粘贴到 echarts.apache.org/examples/zh... 看效果

js 复制代码
option = {
  xAxis: {
    type: 'category',
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      data: [120, 200, 150, 80, 70, 110, 130],
      type: 'bar',
      showBackground: true,
      backgroundStyle: {
        color: 'rgba(180, 180, 180, 0.2)'
      }
    }
  ],
  tooltip: {
    show: true,
    // 模板字符串
    // formatter: 'x轴:{b},y轴:<b>{c}</b><br />啊哈哈',

    // 回调函数返回html
    // formatter: (params) => {
    //   console.log(params);
    //   return `<div>
    //     <span style="color: red;">${params.name}</span>
    //     <span style="font-weight: bold">${params.data}</span>
    //   </div>`;
    // },

    // 回调函数返回Dom
    formatter: (params) => {
      console.log(params);
      const div = document.createElement('div');
      const span = document.createElement('span');
      span.style.color = 'red';
      span.innerText = params.data;
      div.appendChild(span);
      return div;
    }
  }
};

tooltip提供的方式已经可以满足绝大多数场景了,如果不需要太复杂的tooltip,优先还是使用这些方式。

2. 复杂的tooltip内容

如果需要展示较复杂的tooltip,可以使用模板字符串拼接html或是直接用js创建dom的方式来实现。

不过这样写起来十分不方便,还是写Vue组件来得快。

方式一: 使用createApp

首先准备一个组件,注意这里我写了两个props,一个是string,一个是数组:

Tooltip.vue

js 复制代码
<script setup lang="ts">
import {defineProps} from "vue";

defineProps<{ title: string, rows?: { label: string, value: number }[] }>()
</script>

<template>
  <div class="title">{{ title }}</div>
  <table>
    <tr>
      <th>类别</th>
      <th>数据</th>
    </tr>
    <tr v-for="data in rows" :key="data.label">
      <td class="label">{{ data.label }}</td>
      <td class="value">{{ data.value }}</td>
    </tr>
  </table>
</template>


<style scoped>
.title {
  text-align: center;
  font-weight: bold;
}

.label {
  color: #333333;
}

.value {
  font-weight: bold;
  text-align: right;
}

table, td, th {
  border-collapse: collapse;
  border: 1px solid #333333;
}
</style>

这里只是写了简单的table,实际上里面多复杂都行,表单、el-table都行,vue组件中能干的都能往里写

接下来让tooltip接受并渲染这个组件:

App.vue (只提供了核心代码,之后有完整代码)

js 复制代码
// 导入组件
import Tooltip from './Tooltip.vue'

// ECharts的Options
const option = {
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      // Use axis to trigger tooltip
      type: 'shadow' // 'shadow' as default; can also be 'line' or 'shadow'
    },
    formatter: (params) => {
      console.log(params) // 本例中参数是数组,请根据你的实际情况来
      if (params.length) {
        // 准备好tooltip需要的数据
        const title = params[0].axisValueLabel
        const rows = params.map(item => ({label: item.seriesName, value: item.data}))
        // 容器,之后会把组件渲染在容器中
        const div = document.createElement('div')
        // vue文件直接用不行,得创建app实例,同时提供组件的props
        const app = createApp(Tooltip, {title, rows})
        // 将app实例挂载到dom上
        app.mount(div)
        // 将含有组件实例的dom返回给echats
        return div
      }
    }
  },
   // ... 其他echats配置
};

// ... 创建echats实例并设置optons

当当当当!

优点

  • 使用方便,项目不需要特别设置就能用

缺点

  • tooltip的回调实际上十分频繁,每次都创建app实例,性能开销太大
  • createApp的rootProps没有好的类型推断
  • 将会在vue devtools中看到一堆app实例

优势明显,缺陷也很明显。
为了避免每次都创建app实例,可以将createApp(Tooltip)放在回调外只调用一次,确实可以提高性能,但是这样就不能动态改变传递给组件的参数props了
只调用一次,还想动态改变参数也是有办法的,可以给app挂载一个vuex/pinia, 内部使用store获取值,外部使用store修改值

这里不展开了,可以自己去尝试。接下来介绍customElement的方式。

方式二: 使用customElement

customElement是一种允许开发者自定义标签的技术。其兼容性很好,可以放心使用。

如果不了解customElement也没关系,只要知道它是能让浏览器认识并正确渲染你的标签的技术,比如<my-element value="这是我的组件的props">这是我的组件</my-element>

如果用原生去实现一个customElement,那费劲的不如去拼接html字符串。好在现代前端框架都提供了创建customElement的方法,比如Vue的: 在 Vue 中使用自定义元素

注意这里 由于style会被抽离出组件中,如果你不想样式失效,或组件样式被外部影响,那么可以使用自定义元素模式,方式很简单,将组件改为.ce.vue即可:

OK,开整!

首先将上面的Tooltip.vue,重命名为Tooltip.ce.vue(由于title和HTML标签本身的属性冲突了,改名为name了,并且title的颜色改成了红色以示区别,其余代码与Tooltip.vue相同)

js 复制代码
<script setup lang="ts">
import {defineProps} from "vue";

defineProps<{ name: string, rows?: { label: string, value: number }[] }>()
</script>

<template>
  <div class="title">{{ name }}</div>
  <table>
    <tr>
      <th>类别</th>
      <th>数据</th>
    </tr>
    <tr v-for="data in rows" :key="data.label">
      <td class="label">{{ data.label }}</td>
      <td class="value">{{ data.value }}</td>
    </tr>
  </table>
</template>


<style scoped>
.title {
  text-align: center;
  font-weight: bold;
  color: red;
}

.label {
  color: #333333;
}

.value {
  font-weight: bold;
  text-align: right;
}

table, td, th {
  border-collapse: collapse;
  border: 1px solid #333333;
}
</style>

然后在main(或者其他你喜欢的位置)中注册它:

js 复制代码
/**
 * 注册customElement
 */
import {defineCustomElement} from "vue";
import Tooltip from './Tooltip.ce.vue'
import type {VueElementConstructor} from "@vue/runtime-dom";

// 使用vue创建自定义元素
// 如果不需要类型定义那么直接const TooltipCE = defineCustomElement(Tooltip)就可以
// 由于之前定义的title props和所有HTML元素本身就有的title冲突了,所以改为name
export const TooltipCE = defineCustomElement(Tooltip) as VueElementConstructor<{ name?: string, rows: { label: string, value: number }[] }>

// 向浏览器注册自定义标签,标签名为my-tooltip
window.customElements.define('my-tooltip', TooltipCE)

然后就像使用div一样使用它:

js 复制代码
import {TooltipCE} from "./ceRegistry";

// 创建一次tooltipCe的实例,new或者document.createElement('my-tooltip')都可以
const tooltipCE = new TooltipCE()
const option = {
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      // Use axis to trigger tooltip
      type: 'shadow' // 'shadow' as default; can also be 'line' or 'shadow'
    },
    // customElement方式
    formatter: (params) => {
      console.log(params) // 本例中参数是数组,请根据你的实际情况来
      if (params.length) {
        // 准备好tooltip需要的数据
        const title = params[0].axisValueLabel
        const rows = params.map(item => ({label: item.seriesName, value: item.data}))
        // 设置props属性,对于string\number\boolean可以直接设置标签的attribute,也可以在实例上设置
        // 其他标签有的方法它都有
        tooltipCE.setAttribute('name', title)
        // 复杂属性使用实例来设置,比如array/object
        tooltipCE.rows = rows
        return tooltipCE
      }
    }
  },
  // ... 其他Echarts Options
};

当当当当!

看下浏览器dom里什么样:

可以看到我们自定义的标签my-tooltip生效了。

同样的,里面也可以写任何vue里能写的东西。

优点

  • 只创建一个dom实例,性能嘎嘎好
  • 直接通过dom实例修改属性来通信,简单易用
  • 良好的TS类型推断
  • 十分方便使用的插槽

缺点

如果想在Vue模板中使用该自定义标签,还需要特别配置: 跳过组件解析

完整代码

掘金自带的代码片段不好用,直接放github上了 github.com/meitan-li/s...

最后

  • 同样的,利用createAppcustomElement可以在js中使用Vue组件的特性。可以创建一个函数,函数中使用它们来渲染一个dom,再将dom插入到body中,不就可以实现类似Element的ElMessage.success的效果(当然实际要考虑的东西还有很多)!
相关推荐
Devil枫3 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
GIS程序媛—椰子5 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
毕业设计制作和分享5 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
程序媛小果6 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot
从兄6 小时前
vue 使用docx-preview 预览替换文档内的特定变量
javascript·vue.js·ecmascript
凉辰7 小时前
设计模式 策略模式 场景Vue (技术提升)
vue.js·设计模式·策略模式
薛一半8 小时前
PC端查看历史消息,鼠标向上滚动加载数据时页面停留在上次查看的位置
前端·javascript·vue.js
MarcoPage9 小时前
第十九课 Vue组件中的方法
前端·javascript·vue.js
工业互联网专业9 小时前
Python毕业设计选题:基于Hadoop的租房数据分析系统的设计与实现
vue.js·hadoop·python·flask·毕业设计·源码·课程设计
你好龙卷风!!!9 小时前
vue3 怎么判断数据列是否包某一列名
前端·javascript·vue.js