【工具类】Echarts封装 Vue&React

Vue 组件
<template>
    <div :style="{ height: height, width: width }" :class="className" :id="id">div>
template>
<script>
window._CHARTLIST_ = []; // 多个chart
import * as echarts from "echarts"; // 引入还是可以优化的,采用按需引入
import { debounce } from "./lodash-util"; // 自己实现的一个 debounce
export default {
    props: {
        id: {
            type: String,
            required: true,
        },
        className: {
            type: String,
            default: "chart",
        },
        width: {
            type: String,
            default: "100%",
        },
        height: {
            type: String,
            default: "100%",
        },
        options: {
            type: Object,
            required: true,
            default: () => ({}),
        },
    },
    data() {
        return {
            chart: null,
        };
    },
    methods: {
        /* 初始化 echarts */
        initChart() {
            if (!this.chart) {
                const chartDom = document.querySelector(`#${this.id}`);
                this.chart = echarts.init(chartDom);
                this.chart.setOption(this.options, true);
                window._CHARTLIST_.push(this.chart);
                window.addEventListener("resize", this.resize);
            } else {
                this.chart.setOption(this.options, true);
                this.chart.resize();
            }
        },
        /* echarts 自适应 */
        resize() {
            const { chartObj } = this;
            const resizeFunc = function () {
                if (window._CHARTLIST_ && Array.isArray(_CHARTLIST_)) {
                    window._CHARTLIST_.forEach((item) => {
                        item.resize && item.resize();
                    });
                }
            };
            debounce(resizeFunc, 300);
        },
    },
    mounted: function () {
        this.initChart();
    },
    beforeDestroy() {
        if (this.chart) {
            if (window._CHARTLIST_ && Array.isArray(_CHARTLIST_)) {
                window._CHARTLIST_.forEach((item, index) => {
                    item.id === this.chart.id && window._CHARTLIST_.splice(index, 1);
                });
            }
            this.chart = null;
            window.removeEventListener("resize", this.resize);
        }
    },
    watch: {
        options: {
            handler(n) {
                /* 判断传参是否空对象 */
                if (Object.keys(n).length > 0) {
                    this.initChart();
                }
            },
            deep: true,
        },
    },
};
script>
React 组件
import React, { memo } from 'react';
import * as echarts from 'echarts/core';
import { ECharts } from 'echarts/types/dist/core';
import { EChartsOption } from 'echarts/types/dist/shared';
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  LegendComponent,
  DatasetComponent,
  DataZoomComponent,
  ToolboxComponent,
} from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
import { debounce, forEach } from 'lodash';

type domClassName = string[];


interface IEchartProps {
  /** echarts 数据参数 */
  option: EChartsOption;
  domClassName?: domClassName;
  /** echarts 图形组件 */
  charts?: any[];
}

interface IEchartState {
  chart: null | ECharts;
  domClassName: domClassName;
}

/** Echart 图表
 * 注意: 一定记得添加Echarts组件,不然就是白屏
 */
class Echart extends React.Component<IEchartProps, IEchartState> {
  constructor(props: IEchartProps) {
    super(props);
    echarts.use([
      TitleComponent,
      TooltipComponent,
      GridComponent,
      LegendComponent,
      DatasetComponent,
      CanvasRenderer,
      DataZoomComponent,
      ToolboxComponent,
      ...(props.charts || []),
    ]);
   	// 例如:左侧菜单的折叠按钮,点击后需要 resize chart
    this.state = { chart: null, domClassName: ['.collapse', '.header-collapse', ...(props.domClassName || [])] };
  }

  chartRef = React.createRef<HTMLDivElement>();

  componentDidMount() {
    this.initChart();
    this.btnListenResizeEvent('add');
    window.addEventListener('resize', this.resize);
  }

  componentDidUpdate(): void {
    const { option: newOptions } = this.props;
    const { chart } = this.state;
    if (newOptions && chart) chart.setOption(newOptions, true);
  }

  componentWillUnmount() {
    const { chart } = this.state;
    if (chart) chart.dispose();
    this.btnListenResizeEvent('remove');
    window.removeEventListener('resize', this.resize);
  }

  /** 初始化 echarts */
  initChart = debounce(() => {
    if (!this.chartRef.current) return;
    this.setState({ chart: echarts.init(this.chartRef.current) });
  }, 200);

  /** echarts 自适应 */
  resize = debounce(() => this.state.chart?.resize(), 200);

  /** 特殊的的按钮click会修改Echarts的视口大小,监听click事件来自适应Echarts */
  btnListenResizeEvent = debounce((type: 'add' | 'remove') => {
    forEach(this.state.domClassName, (v) => {
      if (type === 'add') {
        document.querySelector(v)?.addEventListener('click', this.resize);
      } else {
        document.querySelector(v)?.removeEventListener('click', this.resize);
      }
    });
  }, 200);

  render(): React.ReactNode {
    return <div style={{ height: '100%' }} ref={this.chartRef} />;
  }
}

export default memo(Echart); // 用memo包一下,*_* 算是个优化吧;



/* ================== 使用 ==================== */

import Echart from '@/components/Echarts';
import { LineChart } from 'echarts/charts'; // 按需导入:这里使用的是折线图

function Demo(){
	const options = { /* ... */} ; // 对应的chart配置
	return (<Echart option={options} charts={[LineChart]} />);
}

你可能感兴趣的:(react.js,vue.js,echarts)