The correct posture of encapsulating echarts in vue project

Posted by ranjita on Sat, 11 Apr 2020 10:41:54 +0200

Why we need to encapsulate echarts

  • Every developer needs to write a complete option configuration from the beginning to the end, which is very redundant
  • In the same project, all kinds of chart design are very similar, even the same, so there is no need to do repeated work all the time
  • Some developers may forget to consider the features of updating data and the adaptability of window zooming. This causes the data to update the echarts view but not to update, and the window zooming causes the echarts graphics to deform

What do I hope this echarts component will look like

  • Business data and style configuration data are separated. I just need to pass in business data
  • Its size is entirely up to the user
  • It will not deform due to scaling, but can adapt well
  • Sometimes the style of a chart may be a little different. I want to keep the flexibility of my own configuration style
  • Update the view correctly no matter what data is passed in
  • If the data I passed in is empty, it can display a null state

Suggestions on the structure of common components

  • When you write it as a public component, I hope it should be like this: separate the configuration table of a single machine into a file, expose a necessary vue single file, and carry a README instruction document. Of course, the document needs to have usage examples and input parameter meanings of the components you write, which is very important in large projects.

vue single file code

  • The complete code is as follows:
<template>
  <div class="chart"></div>
</template>

<script>
import { merge, isEmpty } from "lodash";
import echart from "echarts";
import { BASIC_OPTION, EMPTY_OPTION } from "./default_option";

export default {
  props: {
    //Business data
    dataList: {
      typeArray,
      default() => []
    },
    //Special style customization
    extraOption: {
      typeObject,
      default() => ({})
    }
  },
  data() {
    return {
      chartnull
    };
  },
  methods: {
    /**
     * Add business data to basic style configuration
     * @returns {Object} Complete echart configuration
     */

    assembleDataToOption() {
      return merge(
        {},
        BASIC_OPTION,
        {
          series: [{ datathis.dataList }]
        },
        this.extraOption
      );
    },

    /**
     * Update echart view
     */

    updateChartView() {
      if (!this.chart) return;

      const fullOption = isEmpty(this.dataList)
        ? EMPTY_OPTION
        : this.assembleDataToOption();
      this.chart.setOption(fullOption, true);
    },

    /**
     * When the window zooms, echart dynamically resizes itself
     */

    handleWindowResize() {
      if (!this.chart) return;
      this.chart.resize();
    }
  },
  watch: {
    dataList: {
      deeptrue,
      handler() => this.updateChartView
    }
  },
  mounted() {
    this.chart = echart.init(this.$el);
    this.updateChartView();
    window.addEventListener("resize"this.handleWindowResize);
  },
  beforeDestroy() {
    window.removeEventListener("resize"this.handleWindowResize);
  }
};
</script>


<style lang="less" scoped>
.chart {
  width100%;
  height100%;
}
</style>


Notes on source code

  • In the source code, I use a common function of lodash, merge, which means to recursively merge the source object itself and inherited enumerable properties into the target object. Subsequent source object properties overwrite properties with the same name
  • Another function that I'm lucky to favor is isEmpty. When the business data I've passed in is empty, such as empty array [], undefined, and null, it will be considered as a case of no data. At this time, we will choose an empty state of eckarts configuration, namely empty option
  • When binding to a specific DOM element, I do not use the querySelector to select a class or the id generated by Math.random, because neither of them is absolutely reliable. I directly use the root DOM element $el associated with the current vue example
  • I listen for the change of window size, and add the corresponding event handling function -- resize method of echarts, so that the echarts graph will not be deformed
  • Set the width and height of the corresponding DOM to 100%, so that its size is completely controlled by the container provided by the user
  • The second parameter of setOption method indicates whether the new option passed in does not merge with the old option. By default, it is false, that is, merge. It's obviously not possible. We need to make every business configuration completely independent
  • Naming is very semantic. You can understand it at a glance
  • The flexibility to configure some custom styles independently, i.e. extraOption, is preserved
  • As for the specific meaning of dataList and extraOption, use examples are put in the README.md file for detailed explanation. Users can start at a glance at the document

What should be included in default menu option.js

  • It should include two parts: the basic configuration under normal conditions and the empty configuration under abnormal conditions, as follows:

Component usage example

<template>
  <div class="echart-wrapper">
    <chart-pie :data-list="pieData" :extra-option="option" />
  </div>
</template>


<script>
import ChartPie from "@/components/echarts/echart_pie/EchartPie.vue";

export default {
  name"home",
  data() {
    return {
      //Business data
      pieData: [
        {
          name"watermelon",
          value20
        },
        {
          name"A mandarin orange",
          value13
        },
        {
          name"Carambola",
          value33
        }
      ],
      //Custom configuration override default configuration
      option: {
        color: ["#e033f7""#15c28e""#2267e6"]
      }
    };
  },
  components: {
    ChartPie
  }
};
</script>


<style lang="less" scoped>
.echart-wrapper {
  width300px;
  height300px;
  margin10px auto;
}
</style>


Design sketch

  • When the data is normal, the effect is as follows

  • When there is no data, the effect is as follows

What can be further optimized

  • My hand encapsulation is already a top-quality work. If there is any shortage, I think we can consider introducing the throttle throttling function in lodash to further optimize performance in the aspect of resize throttling.

Topics: Javascript Vue less