在vue项目中封装echarts的步骤


Posted in Vue.js onDecember 25, 2020

为什么需要封装echarts

  • 每个开发者在制作图表时都需要从头到尾书写一遍完整的option配置,十分冗余
  • 在同一个项目中,各类图表设计十分相似,甚至是相同,没必要一直做重复工作
  • 可能有一些开发者忘记考虑echarts更新数据的特性,以及窗口缩放时的适应问题。这样导致数据更新了echarts视图却没有更新,窗口缩放引起echarts图形变形问题

我希望这个echarts组件能设计成什么样

  • 业务数据和样式配置数据分离,我只需要传入业务数据就行了
  • 它的大小要完全由使用者决定
  • 不会因为缩放出现变形问题,而是能很好地自适应
  • 有时候某个图表的样式可能有点不一样,希望能保留自己配置样式的灵活性
  • 无论传入什么数据都能正确地更新视图
  • 如果我传入的数据为空,能展示一个空状态

公共组件结构建议

当你把它书写为一个公共组件时,我我希望它应该是这样:将单独机械的配置表独立成一份文件,暴露一个必要的vue单文件,同时携带一份README说明文档,当然,文档里面需要有关于你写的组件的使用示例和入参含义说明,这在大型项目中非常重要。

在vue项目中封装echarts的步骤

vue单文件代码

echart_pie完整代码如下:

<template>
 <div class="chart"></div>
</template>

<script>
import { merge } from 'lodash';
import echart from 'echarts';
import { BASIC_OPTION } from './default_option';
import { COLOR_ARRAY } from '../color';
import ResizeListener from 'element-resize-detector';

export default {
 name: 'ChartPie',
 props: {
  seriesData: {
   type: Array,
   required: true,
   default: () => []
  },
  extraOption: {
   type: Object,
   default: () => ({})
  }
 },
 data() {
  return {
   chart: null
  };
 },
 watch: {
  seriesData: {
   deep: true,
   handler() {
    this.updateChartView();
   }
  }
 },
 mounted() {
  this.chart = echart.init(this.$el);
  this.updateChartView();
  window.addEventListener('resize', this.handleWindowResize);
  this.addChartResizeListener();
 },
 beforeDestroy() {
  window.removeEventListener('resize', this.handleWindowResize);
 },
 methods: {
  /**
   * 将业务数据加入到基础样式配置中
   * @returns {Object} 完整的echart配置
   */
  assembleDataToOption() {
   const formatter = name => {
    const total = this.seriesData.reduce((acc, cur) => acc + cur.value, 0);
    const data = this.seriesData.find(item => item.name === name) || {};
    const percent = data.value
     ? `${Math.round((data.value / total) * 100)}%`
     : '0%';

    return `{a|${name}}{b|${percent}}`;
   };

   return merge(
    {},
    BASIC_OPTION,
    { color: COLOR_ARRAY },
    {
     legend: { formatter },
     series: [{ data: this.seriesData }]
    },
    this.extraOption
   );
  },

  /**
   * 对chart元素尺寸进行监听,当发生变化时同步更新echart视图
   */
  addChartResizeListener() {
   const instance = ResizeListener({
    strategy: 'scroll',
    callOnAdd: true
   });

   instance.listenTo(this.$el, () => {
    if (!this.chart) return;
    this.chart.resize();
   });
  },

  /**
   * 更新echart视图
   */
  updateChartView() {
   if (!this.chart) return;

   const fullOption = this.assembleDataToOption();
   this.chart.setOption(fullOption, true);
  },

  /**
   * 当窗口缩放时,echart动态调整自身大小
   */
  handleWindowResize() {
   if (!this.chart) return;
   this.chart.resize();
  }
 }
};
</script>

<style lang="less" scoped>
.chart {
 width: 100%;
 height: 100%;
}
</style>

index完整代码如下:

<template>
 <van-empty v-if="isSeriesEmpty" />
 <chart-pie v-else v-bind="$props" />
</template>

<script>
import { isEmpty } from 'lodash';
import ChartPie from './echart_pie.vue';

export default {
 name: 'EchartPie',
 components: { ChartPie },
 props: ChartPie.props,
 computed: {
  isSeriesEmpty() {
   return (
    isEmpty(this.seriesData) || this.seriesData.every(item => !item.value)
   );
  }
 }
};
</script>

关于源码的说明

  • 在源码中,我用到了lodash的一个公共函数merge,它表示递归合并来源对象自身和继承的可枚举属性到目标对象。后续的来源对象属性会覆盖之前同名的属性
  • 另外一个有幸被我宠幸的函数是isEmpty,当我传入的业务数据为空时,比如空数组[]、undefined、null时,都会被认为这是一个无数据的情况,这时候我们就展示一个空状态的组件,它可能由一张背景图构成;
  • 在绑定到具体的DOM元素时,我没有用querySelector选择器去选择一个类或者是用Math.random生成的id,因为这两者都不是绝对可靠的,我直接使用当前vue示例关联的根DOM 元素$el
  • 我监听窗口大小的变化,并为这种情况添加对应的事件处理函数--echarts自带的resize方法,使echarts图形不会变形
  • 将对应DOM的宽高设为100%,让其大小完全由使用者提供的容器控制
  • setOption方法的第二个参数表示传入的新option是否不与之前的旧option进行合并,默认居然是false,即合并。这显然不行,我们需要每次的业务配置都是完全独立的
  • 命名非常语义化,一看就懂
  • 保留了自己需要单独配置一些定制样式的灵活性,即extraOption

default_option.js应该包括哪些内容

正常情况下暴露一个基础配置BASIC_OPTION就可以了
element-resize-detector是干啥的?
这是一个用于监听DOM元素尺寸变化的插件。我们已经对窗口缩放做了监听,但是有时候其父级容器的大小也会动态改变的。

我们对父级容器的宽度进行监听,当父级容器的尺寸发生变化时,echart能调用自身的resize方法,保持视图正常。

当然,这个不适用于tab选项卡的情况,在tab选项卡中,父级容器从display:none到有实际的clientWidth,可能会比注册一个resizeDetector先完成,所以等开始监听父级容器resize的时候,可能为时已晚。

在tab选项卡中很容易出现这种场景:

在vue项目中封装echarts的步骤

看起来非常的逗,只有一点点大。解决这个问题,最有效的方法还是在切换选项卡时手动去通过ref获取echart实例,并手动调用resize方法,这是最安全的,也是最有效的。

组件使用示例

<template>
<div class="echart-pie-wrap">
 <echart-pie :series-data="dataList" :extra-option="extraOption" />
</div>
</template>

<script>
import EchartPie from '@/components/echarts/echart_pie';

export default{
 components: { EchartPie },
 data() {
  return {
   dataList: [
    {
     name: "西瓜",
     value: 20
    },
    {
     name: "橘子",
     value: 13
    },
    {
     name: "杨桃",
     value: 33
    }
   ],
   extraOption: {
     color: [ '#fe883a', '#2d90d1','#f75981', '#90e2a9']
   }
  }
 }
}

</script>

<style lang="less" scoped>
.echart-wrapper {
 width: 300px;
 height: 300px;
 margin: 10px auto;
}
</style>

效果图

当数据正常时,效果如下

在vue项目中封装echarts的步骤

当无数据时,效果如下

在vue项目中封装echarts的步骤

关于灵活性

这里面提供的封装只是一种大概的思路,实际项目中,可能会与这里出现偏差。软件开发没有银弹可言,非常适合我这个项目的,可能在细节上不太适合看这篇文章的你。但是思路是类似的,可以对照着做一些调整。

以上就是在vue项目中封装echarts的步骤的详细内容,更多关于vue项目中封装echarts的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
vue使用echarts图表自适应的几种解决方案
Dec 04 Vue.js
vue调用微信JSDK 扫一扫,相册等需要注意的事项
Jan 03 Vue.js
通过vue.extend实现消息提示弹框的方法记录
Jan 07 Vue.js
vscode自定义vue模板的实现
Jan 27 Vue.js
开发一个封装iframe的vue组件
Mar 29 Vue.js
vue前端工程的搭建
Mar 31 Vue.js
Vue如何实现组件间通信
May 15 Vue.js
Vue实现导入Excel功能步骤详解
Jul 03 Vue.js
Vue3如何理解ref toRef和toRefs的区别
Feb 18 Vue.js
vue实现可以快进后退的跑马灯组件
Apr 08 Vue.js
vue3引入highlight.js进行代码高亮的方法实例
Apr 08 Vue.js
vue项目如何打包之项目打包优化(让打包的js文件变小)
Apr 30 Vue.js
vue中封装axios并实现api接口的统一管理
Dec 25 #Vue.js
Vue 简单实现前端权限控制的示例
Dec 25 #Vue.js
Vue通过阿里云oss的url连接直接下载文件并修改文件名的方法
Dec 25 #Vue.js
vue使用require.context实现动态注册路由
Dec 25 #Vue.js
vue 使用rules对表单字段进行校验的步骤
Dec 25 #Vue.js
vue 实现基础组件的自动化全局注册
Dec 25 #Vue.js
vue 使用class创建和清除水印的示例代码
Dec 25 #Vue.js
You might like
用PHP实现小写金额转换大写金额的代码(精确到分)
2012/01/10 PHP
php获取ip的三个属性区别介绍(HTTP_X_FORWARDED_FOR,HTTP_VIA,REMOTE_ADDR)
2012/09/23 PHP
ThinkPHP3.1新特性之命名范围的使用
2014/06/19 PHP
JavaScript中“基本类型”之争小结
2013/01/03 Javascript
JavaScript中rem布局在react中的应用
2015/12/09 Javascript
Bootstrap开关(switch)控件学习笔记分享
2016/05/30 Javascript
对Js OOP编程 创建对象的一些全面理解
2016/07/26 Javascript
使用 bootstrap modal遇到的问题小结
2016/11/09 Javascript
Angular模版驱动表单的使用总结
2018/05/05 Javascript
Vue作用域插槽slot-scope实例代码
2018/09/05 Javascript
百度小程序自定义通用toast组件
2019/07/17 Javascript
Vue实现图书管理小案例
2020/12/03 Vue.js
深入剖析Python的爬虫框架Scrapy的结构与运作流程
2016/01/20 Python
mysql 之通过配置文件链接数据库
2017/08/12 Python
Python实现一个简单的验证码程序
2017/11/03 Python
EM算法的python实现的方法步骤
2018/01/02 Python
对python 多个分隔符split 的实例详解
2018/12/20 Python
Python学习笔记之错误和异常及访问错误消息详解
2019/08/08 Python
python之yield和Generator深入解析
2019/09/18 Python
Python hmac模块使用实例解析
2019/12/24 Python
安装pyinstaller遇到的各种问题(小结)
2020/11/20 Python
python实现猜拳游戏项目
2020/11/30 Python
副总经理工作职责
2013/11/28 职场文书
前台文员个人求职信范文
2014/01/05 职场文书
国际贸易专业个人鉴定
2014/02/22 职场文书
2014两会学习心得:时代的发展
2014/03/17 职场文书
爱国主义教育活动总结
2014/05/07 职场文书
党的群众路线教育实践活动整改落实情况报告
2014/10/28 职场文书
教师先进个人材料
2014/12/17 职场文书
离婚协议书样本
2015/01/26 职场文书
党校个人总结
2015/03/04 职场文书
项目经理助理岗位职责
2015/04/13 职场文书
社区扶贫帮困工作总结
2015/05/20 职场文书
2015年十月一日放假通知
2015/08/18 职场文书
2019年七夕情人节浪漫祝福语大全!
2019/08/08 职场文书
简单谈谈Python面向对象的相关知识
2021/06/28 Python