Vue:在Vue中使用echarts

前言

公司的项目中需要对数据做可视化处理,高级点的D3.js目前还没接触到,因此选用了大众化的Echarts, 在vue的生态系统中已经有实现好的vue-echarts,但是使用现成的就意味着必须使用它定制好的数据结构,我也没办法对他进行一些修改。我个人也偏向于原生JS编程,因此没有采用,而是自己在vue中实现了对数据的可视化处理,先来看看效果图

以下数据已做脱敏处理

echarts Bar.png

echartsPie.png

echartsMap.png

这是目前用到的三种图。 可以看到,我在图表的外部添加了标题及说明,以及右侧的选择框组件,视图可以根据选择的不同,图表进行动态切换(echarts也是数据驱动),这就是个人定制化的好处

总体数据流向

所有的数据都是动态获取的,由前端向后台请求。当然请求数据肯定不会放在图表组件中,而是放在了外部。因为架构设计的不合理(MD前端就我一个人!),因此前期获取数据及存取数据的方式,和后期也较大的不同。

1. 存放在vuex中

这么大型的项目,vuex少不了。在前面的组件中,一次请求数据,然后将数据存储到了vuex中,echarts组件再从vuex中获取数据。这样的做法可能代码要稍微复杂点,但是数据存储在vuex中是随时可见的,我们也能随时保存获取的结果,对这些数据可以添加收藏也可以加入缓存中,下次再请求可以先从缓存调用。 (然而这只是我前端的想法,后台已经实现了对请求数据的缓存)

2. 存放在组件中,再分派到echarts组件

再对数据进行还原的时候(我的收藏,最近浏览),因为不需要保存或者收藏数据,我就直接用一个父组件请求,然后再分发到echarts组件,这样没有经过vuex,代码想多要少些。

组件代码

<template>
  <div class="r-echarts-line">
    <div class="top">
      <div class="title">
        {{origin.title}}
      </div>
      <div class="select-list">
        <Select style="width:120px;margin-right:.5rem" v-model="pagePick">
          <Option v-for="item in origin.page_select" :key="item" :value="item.val">{{item.name}}</Option>
        </Select>
        <Select style="width:120px" v-model="typePick">
          <Option v-for="item in typeList" :value="item.name" :key="item">{{item.name}}</Option>
        </Select>
      </div>
    </div>
    <div class="des">说明:符合于本次筛选条件的共有<span class='tips'>{{origin.desc}}</span>条<span style="font-weight:700;color:black">职位信息</span>。</div>
    <div class="bottom" id="echart" ref="mychart">

    </div>
  </div>
</template>

这是组件的html部分,可以看见top以及des是我自己添加的,bottom才是核心,也是整个echarts展示的部分,注意这里添加了ref,在script的代码中,我们将通过这个钩子,将DOM挂载到echarts中

<script>
// echarts相关
let echarts = require('echarts/lib/echarts');
require('echarts/lib/chart/bar');
require('echarts/lib/component/tooltip');
require('echarts/lib/component/toolbox');
require('echarts/lib/component/legend');
require('echarts/lib/component/markLine');

export default {
  name: 'r-echarts-line',
  data () {
    return {
    typePick: '数值',
    typeList: [
      {
        name: '数值'
      },
      {
        name: '百分比'
      }
    ],
    pagePick: 0,
    // myChart实例
      myChart: {},
      percent: {
        label: {
          normal: {
            show: true,
            position: 'inside',
            formatter: '{c}%'
          }
        }
      },
      numeric: {
        label: {
          normal: {
            show: true,
            position: 'inside',
            formatter: '{c}'
          }
        }
      }
    }
  },
  props: {
    index: {
      required: true,
      type: Number
    },
    data: {
      required: true,
      type: Object
    }
  },
  mounted () {
    this.setEchart();
  },
  updated () {
    if (!this.myChart) {
      this.setEchart();
    }
    this.chartChange();
  },
  computed: {
    origin () {
      return this.data;
    },
    opt() {
      let that = this;
      let obj = {
        color: ['#606c94'],
        tooltip: {
        },
        toolbox: {
            show: true,
            feature: {
                saveAsImage: {show: true}
            }
        },
        label: {
          normal: {
            show: true,
            position: 'inside',
            formatter: '{c}'
          },
          emphasis: {
            show: true
          }
        },
        xAxis: {
          type: 'value',
        },
        yAxis: {
          data: that.origin[that.type][that.pagePick].key,
          axisLabel: {
            interval: 0,
            rotate: -30
          }
        },
        series: [{
          name: that.origin.title,
          type: 'bar',
          data: that.origin[that.type][that.pagePick].val,
          barMaxWidth: '30',
          markLine: {
            data: [
              {type: 'average', name: '平均值'}
            ]
          }
        }]
      }
      return obj;
    },
    type () {
      if (this.typePick == '数值') {
        return 'numeric';
      } else if (this.typePick == '百分比') {
        return 'percent';
      } else {
        return 'numeric';
      }
    }
  },
  methods: {
    setEchart () {
      let dom = this.$refs.mychart;
      this.myChart = echarts.init(dom);
      this.myChart.setOption(this.opt);
    },
    chartChange () {
      this.myChart.setOption(this.opt);
      if (this.typePick == '百分比') {
        this.myChart.setOption(this.percent);
      }
      if (this.typePick == '数值') {
        this.myChart.setOption(this.numeric);
      }
    }
  }
}
</script>

首先我引入了需要的echarts组件,这个部分通过npm i echarts -S添加。 接着data部分我设置了那些将会引起变化的参数。需要注意的是,我并没有将echarts的opt部分写入到data中,因为整个图表是基于数据驱动的,并且随时会发生变化,因此我将opt设置为计算属性computed,这样opt将会根据我的选择动态变化,echarts也将动态响应,mychart用于接收echarts生成的图表实例,再参数变换的时候将会起作用。

props部分是我接收到的参数,这个组件时基于前面我讲的第二种方式——父组件获取数据分发,data是父组件分发给echarts的数据源。

暂时忽略两个Vue生命周期钩子, 后面讲

计算属性中设置了两个属性,origin和opt,注意这个origin很重要,通过它我将opt项与复杂的数据解耦,无论外面的数据怎么换,opt只关心origin的值,而这个opt在两种数据获取的方式中是不一样的,使用vuex的方式,origin将会直接从vuex中获取数据。这样一定程度上也实现了组件的复用。 opt就是该图表组件的设置项了,这个参数按照官网给的配置,自己搭配即可。

接下来是methods部分,setEchart将会完成对整个图表的初始化,通过this.$refs获取DOM实例,再由echars生成实例并绑定在data中的mychart选项。 chartChange是用来响应我自定义组件的变化的,针对选框的不同将会有不同的显示情况。在这里是百分比和数据的切换

接着是前面忽略的生命周期部分 mounted里使用setEchart方法,初始化图表组件,一定要在这里使用该方法,否则会找不到DOM

updated周期里是响应参数变化的方法,首先检测该实例有没有生成(单页应用因为用户可能存在的误操作,很可能导致实例没有生成,这里检测是很有必要的),接着在vue中的数据发生改变时运行chartChange方法,注意,我的选择框是没有绑定事件的,只是通过v-model改变了参数,然后opt动态响应了参数的变化。当opt的参数变化的时候,updated中的方法就会执行,echarts也会动态响应。这个就是使用基于数据驱动vue最精巧的地方,避免了通过事件调用echartChange方法。也是vue中使用echarts核心的一环

另外还有一个就是获取地图参数的,并不用在官网里下载,提供的npm包里就有,按需引用就好了(使用官网的js版本会报错没找到echarts)

import echarts from 'echarts/lib/echarts';
import 'echarts/lib/chart/map';
import 'echarts/map/js/china.js';

style部分就没什么好聊的了,只需要记住一点,必须显式指定加载echarts 的DOM的宽度和高度

父组件对echarts组件的调用

调用组件的方法按照常规组件调用就好了,只是因为echarts加载数据绘制需要耗费不少时间,我们可能需要通过keep-alive保存组件在内存中,避免切出去的时候被释放了。另外可能一个页面需要展示多个echarts类型组件,这里考虑使用component动态组件

<template>
  <div class="focus-echarts-wrap">
    <keep-alive>
      <component :is="currentView" :index="focusType"></component>
    </keep-alive>
  </div>
</template>

其他问题

在生成柱状图和饼状图的时候,加载时间并不算太慢。只是在加载地图类型的时候,尤其是我在生成中国地图的时候,达到了10s+的延迟,并且阻塞的是主线程,这段时间用户是无法操作的,相当于卡顿的情况。 该数据是在32bit的QQ浏览器上测得的,同事用的64bit的谷歌浏览器会好一点。

初步判断是echarts的问题。当然也因为是在dev模式下,可能和我打开了vuex和事件的监测有关。具体的延迟时间我会在线上版本测试,希望情况会好转吧,不然BOSS又要。。。。。。。。

就是这样:)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏极客编程

Angular 5 快速入门与提高

尽管被称为Angular5,实际上它只是这个诞生于2012年的前端框架的的第四个版本:

20020
来自专栏Bug生活2048

利用云开发优化博客小程序(二)——评论功能

这次迭代主要是完善了评论功能「不知道审核能不能过」,一开始觉得很快能搞定,然而真正开发的时候还是碰到很多问题,这篇文章既是回顾总结,也是记录下自己在开发过程中遇...

44030
来自专栏老付的网络博客

Sumblie Text 3 竖排选择快捷键

前段时间一直在找如何使用Sublime text 3使用竖排编辑的文字的快捷键,一直都在傻傻的使用右键+Shift的组合方式使用。 今天找的了使用方法:

21020
来自专栏跟着阿笨一起玩NET

多台电脑共享一套鼠标键盘的免费方法

本文转载:http://www.cnblogs.com/fangpage/archive/2011/07/22/inputdirector.html

2.7K10
来自专栏葡萄城控件技术团队

哪些JavaScript IDE最好用?

阅读本文之前,分享大家一张图片,看图会发现JavaScript开发需求最高,占比达到42.84%,因此掌握JavaScript语言好工作就不愁啦,工欲善其事必先...

30650
来自专栏phodal

React Native 持续部署实践— push 代码构建出新版的 Growth

最近我们正在使用 React Native 来重写 Growth 应用,GitHub 地址:growth-ng 。作为一个『咨询师』,我要再一次地切换技术栈,从...

27250
来自专栏张戈的专栏

修复WordPress升级4.2外观菜单中显示选项无法点击问题

WordPress 升级 4.2 之后,目前发现存在如下 3 个问题: 有部分主题的前台会产生大量的 404 错误请求(站外资源); 评论表情名称变更导致表情无...

30660
来自专栏python+iOS学习交流

7年iOS架构师教你如何快速提高并掌握 iOS开发核心技能

首先你要花点时间针对objective-c语言的学习;毕竟这个是iOS开发的基础(你也可以尝试用Swift,但此项目只是针对OC),编程套路其实都是差不多,多写...

17110
来自专栏惶心 - 技术博客

添加全站 APlayer 播放器

之前 @狗子 在群里秀他的 Aplayer,还 post 了博文。然后我一看,哇!这就是我想要的!

71340
来自专栏乐百川的学习频道

在计算机中安装Manjaro

前几天我为了尝鲜在电脑中安装了大蜥蜴(OpenSuse),新鲜期过了之后我准备换回原来的ArchLinux,结果发现大蜥蜴把原来的Grub设置覆盖了。如果要恢复...

98080

扫码关注云+社区

领取腾讯云代金券