业务需求中需要实时(最近1分钟)展示数据变化。通过echarts图表注入,定时拉取数据,渲染图表,展示给用户。 这篇记录图表使用。
插件使用 vue 中插件使用遵循以下原则:
插件是一个对象,必须暴露一个 install 方法
插件本身是一个函数,则它将被视为 install 方法。
install 方法将以应用实例作为第一个参数被调用。传给 use 的其他 options 参数将作为后续参数传入该安装方法。 当在同一个插件上多次调用此方法时,该插件将仅安装一次。
echart 使用逻辑
获取 dom 节点
初始化实例
在 dom 渲染后进行 setOption
设置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 , 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 组件对象
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
时,使用的是封装好的组件。
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
中向仪表盘注入数据,初次使用时我直接这样写:
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 中的数据,图表绘制成功。
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 = ( ) => { }const drawTimingFn = ( ) => { setData (); drawTiming.value = window .setInterval (() => { setData (); }, 60000 ); }onMounted (() => { drawTimingFn () })onUnmounted (() => { clearInterval (drawTiming.value ) })
UI 渲染
echarts 由数据驱动,数据的改变驱动图表展现的改变。 动态数据的实现:获取数据,填入数据,echarts 会找到两组数据之间的差异然后通过合适的动画去表现数据的变化。
vue 由数据驱动,数据改变驱动视图变化。 vue 在更新 DOM 时是异步执行的。当数据发生变化,vue 将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新。
vue 有一种渲染优化策略 nextTick
,也是性能优化手段,基于 JS 执行机制实现。等待同一事件循环中的所有数据变化完成之后(一直修改相同数据,异步操作队列还会去重),再将队列中的事件拿来进行处理,进 DOM 的更新,此时 DOM 只需要更新一次。 vue 中我们改变数据时不会立即触发视图,如果需要实时获取到最新的 DOM ,可以手动调用 nextTick
。
在以下情况下,会用到 nextTick
: (1) 在数据变化后执行的某个操作,而这个操作需要使用随数据变化而变化的DOM结构的时候,这个操作就需要方法在 nextTick()
的回调函数中。 (2) 在 vue 生命周期中,如果在 created()
钩子进行 DOM 操作,也一定要放在 nextTick()
的回调函数中。因为在 created()
钩子函数中,页面的 DOM 还未渲染。Vue .nextTick (function ( ) { })Vue .nextTick () .then (function ( ) { })
附录 官方文档 插件 echarts官网示例 各种插件