echarts组件封装逻辑

607 阅读1分钟

为啥要封装该组件?

1.开发运行管理和容量管理的时候发现 echarts 用的情况特别多,时不时会出现 echarts 数据取到但是页面渲染延时页面无法显示的问题。尤其是遍历组件的时候,单纯通过原始方法 id 类获取元素无法达到效果;

2.在同一个项目中,各类图表设计十分相似,甚至是相同,没必要一直做重复工作

3、可能有一些开发者忘记考虑 echarts 更新数据的特性,以及窗口缩放时的适应问题。这样导致数据更新了 echarts 视图却没有更新,窗口缩放引起 echarts 图形变形问题


目的

1.业务数据和样式配置数据分离,我只需要传入业务数据就行了

2.它的大小要完全由使用者决定

3.不会因为缩放出现变形问题,而是能很好地自适应

4.有时候某个图表的样式可能有点不一样,希望能保留自己配置样式的灵活性

5.无论传入什么数据都能正确地更新视图

6.如果我传入的数据为空,能展示一个空状态


组件开发建议

1.单独机械的配置表独立成一份文件,暴露一个必要的vue单文件,同时携带一份README说明文档

2.使用loadash 里面的系列api(isEmpty,merge等)


代码(子组件)


<template>

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

</template>

<script>

import { merge,isEmpty } from 'lodash';

import echart from 'echarts';

import { BASIC_OPTION } from './default_option'; //基础配置BASIC_OPTION  附1

import { COLOR_ARRAY } from '../color'; //基础颜色  附2

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.$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>

代码(子组件) 无数据状态


<template>

  <div class="echart-clothes-err">

    <img src="@/assets/svg/nodata.svg" />

    <div class="md-planet-font">暂无数据</div>

  </div>

</template>

<script>

/* eslint-disable */

// import x from ''

export default {

  name: "noData",

  data() {

    return {};

  },

  components: {},

  computed: {},

  watch: {},

  methods: {},

  beforeMount() {},

  mounted() {},

  activated() {}

};

</script>

<style lang='less' scoped>

/* @import url(); 引入css类 */

.echart-clothes-err {

  display: flex;

  height: 100%;

  position: relative;

  flex-direction: column;

  align-items: center;

  justify-content: center;

  .md-planet {

    font-size: 38px;

    color: #cccccc;

  }

  .md-planet-font {

    font-size: 12px;

    color: #cccccc;

    margin-top: -50px;

  }

  img {

    display: flex;

    height: 180px;

  }

}

</style>

代码 (父组件)


template>

  <no-data 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>