vue3笔记(6-1)echarts图表使用

业务需求中需要实时(最近1分钟)展示数据变化。通过echarts图表注入,定时拉取数据,渲染图表,展示给用户。
这篇记录图表使用。

插件使用

vue 中插件使用遵循以下原则:

  1. 插件是一个对象,必须暴露一个 install 方法
  2. 插件本身是一个函数,则它将被视为 install 方法。

install 方法将以应用实例作为第一个参数被调用。传给 use 的其他 options 参数将作为后续参数传入该安装方法。
当在同一个插件上多次调用此方法时,该插件将仅安装一次。

echart 使用逻辑

  1. 获取 dom 节点
  2. 初始化实例
  3. 在 dom 渲染后进行 setOption 设置
    1
    2
    3
    4
    5
    var chartDom = document.getElementById('main')!;
    var myChart = echarts.init(chartDom);
    var option: EChartsOption;
    option = {...}
    option && myChart.setOption(option);

echarts 插件

要将 echarts 作为插件在 vue 项目中使用,首先在 index.tsx 中进行 echart 插件的封装,使其符合 vue 组件的使用方式,在生命周期中创建、渲染、更新以及销毁。
后续所有传入参数必须遵循 PropsType 中的配置。
chartRef 用来指向渲染图表的 DOM 节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import theme from '@/common/echart/style/theme.js' // 引入默认主题
import * as echarts from 'echarts'

const PropsType = {
id: String, // 图表唯一 id
className: { // 图表类名
type: String,
default: 'chart'
},
width: { // 图表宽度
type: String,
require: true
},
height: { // 图表高度
type: String,
require: true
},
options: { // 图表数据项
type: Object,
default: () => ({}),
}
} as const

export default defineComponent({
name: 'Echarts',
props: PropsType,
setup(props, { expose }) {
const chartRef = ref<HTMLElement>()
const charts = {
chart: null
}
const initChart = (data?: any, clearCaching = false) => {
if (data || props.options) {
charts.chart.setOption(data || props.options, clearCaching)
}
}

onMounted(() => {
echarts.registerTheme('myTheme', theme) // 覆盖默认主题
charts.chart = echarts.init(chartRef.value, 'myTheme')
initChart()
})

onBeforeUnmount(() => {
charts.chart.dispose()
charts.chart = null
})

watch(
() => props.options,
val => {
val && initChart(val)
},
{
deep: true
}
)

expose({ // 对外暴露接口
chartRef,
initChart
});

return () => {
const { id, className, height, width } = props
return <div
ref={chartRef}
id={id as string}
class={className as string}
style={{
'height': height as string,
'width': width as string
}}
/>
}
}
})

componentInstall.ts 中定义一个有 install 方法的 component 组件对象

1
2
3
4
5
6
7
8
9
import type { DefineComponent } from 'vue'
import Echart from './echartCanvas/index'

const component = Object.create(null)

component.install = function (vue: DefineComponent) {
vue.component('echart', Echart)
}
export default component

main.ts 中添加可全局使用的 ComponentEchart ,后续 import Echart 时,使用的是封装好的组件。

1
2
3
4
import ComponentEchart from "@/components/componentInstall";

const app = createApp(App);
app.use(ComponentEchart);

图表组件使用

这里的例子是用仪表盘来表示温度,展示 GPU 运行的实际温度。 temperature 设置中 shutdown(95)、slowdown(92)、max operation(85),实时获取当前温度 current。

最终效果如下:
仪表盘图

首先进行仪表盘的绘制配置,通过draw.tsx实现。
使用watch监听数据改变,手动触发更新(图表初始化)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import * as echarts from 'echarts'
const PropsType = {
cdata: {
type: Object,
require: true
}
} as const

export default defineComponent({
props: PropsType,
setup(props) {
const chartRef = ref()
let options = {}

watch(
() => props.cdata,
(val: any) => {
options = {
series: [
{
type: 'gauge',
...
data: val.current
},
{
type: 'gauge',
...
data: val.maxoperation
},
{
type: 'gauge',
...
data: val.slowdown
}
]
}
// 手动触发更新,通过初始化参数打入数据
if (chartRef.value) {
chartRef.value.initChart(options)
}
},
{
immediate: true,
deep: true
}
)

return () => {
const height = "450px"
const width = "200px"

return <div>
<echart ref={chartRef} height={height} width={width} />
</div>
}
}
})

index.jsx 中向仪表盘注入数据,初次使用时我直接这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default defineComponent({
components: { Draw },
setup() {
const cdata = reactive({
current: [{ value: 24 }],
maxoperation: [{ value: 85 }],
slowdown: [{ value: 92 }],
})
return () => {
return <div>
<Draw cdata={cdata} />
</div>
}
}
})

出现了图表没有渲染的问题,仔细检查后,发现 echarts 没有实例化(可能是没有数据)。
修改 index.jsx ,设置一个 setData 方法,在 DOM 挂载时修改 cdata 中的数据,图表绘制成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
setup() {
const cdata = reactive({
current: [],
maxoperation: [{ value: 85 }],
slowdown: [{ value: 92 }],
})

const setData = () => {
cdata.current.push({ value: 24 })
}

onMounted(() => {
setData()
})
}

需要注意的是代码更新会触发重新编译,但是浏览器没有主动刷新,需要手动刷新浏览器。

定时拉取数据

在本项目中,需要实时(最近1分钟)展示数据变化,采取的方式是通过定时器setInterval实现,每隔60s发送一次请求。
组件挂载时开启定时器,组件卸载时及时清除定时器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const drawTiming = ref(0)
const setData = () => {
//请求数据改变
}
// 定时函数,60秒刷新一次
const drawTimingFn = () => {
setData();
drawTiming.value = window.setInterval(() => {
setData();
}, 60000);
}

onMounted(() => {
drawTimingFn()
})

onUnmounted(() => {
clearInterval(drawTiming.value)
})

UI 渲染

  1. echarts 由数据驱动,数据的改变驱动图表展现的改变。
    动态数据的实现:获取数据,填入数据,echarts 会找到两组数据之间的差异然后通过合适的动画去表现数据的变化。
  2. vue 由数据驱动,数据改变驱动视图变化。
    vue 在更新 DOM 时是异步执行的。当数据发生变化,vue 将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新。

event loop

  1. vue 有一种渲染优化策略 nextTick ,也是性能优化手段,基于 JS 执行机制实现。等待同一事件循环中的所有数据变化完成之后(一直修改相同数据,异步操作队列还会去重),再将队列中的事件拿来进行处理,进 DOM 的更新,此时 DOM 只需要更新一次。
    vue 中我们改变数据时不会立即触发视图,如果需要实时获取到最新的 DOM ,可以手动调用 nextTick
  2. 在以下情况下,会用到 nextTick
    (1) 在数据变化后执行的某个操作,而这个操作需要使用随数据变化而变化的DOM结构的时候,这个操作就需要方法在 nextTick() 的回调函数中。
    (2) 在 vue 生命周期中,如果在 created() 钩子进行 DOM 操作,也一定要放在 nextTick() 的回调函数中。因为在 created() 钩子函数中,页面的 DOM 还未渲染。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Vue.nextTick(function () {
    // DOM 更新了
    })

    // 作为一个 Promise 使用
    Vue.nextTick()
    .then(function () {
    // DOM 更新了
    })

附录

官方文档 插件
echarts官网示例
各种插件


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!