4

Chieffo的个人空间

 3 years ago
source link: https://my.oschina.net/Chieffo/blog/4985955
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
记录一次 Vue 组件封装过程

开发前端项目过程中,有大量的图表数据需要展示,为了更方便地使用以及代码的复用,于是基于ECharts封装了各种各样的图表组件。直接使用封装好的组件对于刚接触我们项目的人来说会比较友好,而理解组件的封装思路和封装细节,将会有助于我们更加娴熟地运用组件到实践中。本文将会以雷达图为案例,一步步讲解在vue项目中如何使用echart,如何将其封装为能重复调用的组件。

在开始之前,我们先按照正常的组件注册流程,在项目 components 目录中新建一个名为 radar-chart 的组件,然后在一个 Demo 页面引入该组件使用。

新建的 radar-chart 组件内容:

// radar-chart.vue (子组件)
<template>
    <div style="width: 100%; height: 100%;"></div>
</template>

<script>
export default {
    name: 'radar-chart'
};
</script>

<style scoped>

</style>

Demo 页面代码:

// demo.vue (父组件)
<template>
    <div style="border: 1px solid black; width: 400px; height: 300px; margin: 5px;">
        <radar-chart></radar-chart>
    </div>
</template>

<script>
import radarChart from '@/components/proj-components/echarts/radar-chart';
export default {
    name: 'radar-chart-demo',
    components: {
        radarChart,
    },
};
</script>

<style scoped>

</style>

Demo 页面效果图一: file

初始化图表

准备工作完成之后,我们要做的是引入 ECharts,并在组件中初始化一个 ECharts 的实例,这里可以先照搬官网的实例和数据。 (1)在 radar-chart.vue 引入 ECharts :

// radar-chart.vue (子组件)
import echarts from 'echarts';

(2)在 methods 中创建图表配置数据的方法,数据格式参考 Echarts 官网:

// radar-chart.vue (子组件)

    methods: {
        // 初始化图表配置
        initOption() {
            let vm = this;
            vm.option = {
                title: {
                    text: '基础雷达图'
                },
                tooltip: {},
                legend: {
                    data: ['预算分配(Allocated Budget)', '实际开销(Actual Spending)']
                },
                radar: {
                    // shape: 'circle',
                    name: {
                        textStyle: {
                            color: '#fff',
                            backgroundColor: '#999',
                            borderRadius: 3,
                            padding: [3, 5]
                        }
                    },
                    indicator: [{ name: '销售(sales)', max: 6500}, { name: '管理(Administration)', max: 16000}, { name: '信息技术(Information Techology)', max: 30000}, { name: '客服(Customer Support)', max: 38000}, { name: '研发(Development)', max: 52000}, { name: '市场(Marketing)', max: 25000}]
                },
                series: [{
                    name: '预算 vs 开销(Budget vs spending)',
                    type: 'radar',
                    // areaStyle: {normal: {}},
                    data: [{value: [4300, 10000, 28000, 35000, 50000, 19000], name: '预算分配(Allocated Budget)'}, {value: [5000, 14000, 28000, 31000, 42000, 21000], name: '实际开销(Actual Spending)'}]
                }]
            };
        },
    },

(3)初始化图表: 在组件钩子 mounted 方法中:

// radar-chart.vue (子组件)
    mounted() {
        this.initOption();
        this.$nextTick(() => { // 这里的 $nextTick() 方法是为了在下次 DOM 更新循环结束之后执行延迟回调。也就是延迟渲染图表避免一些渲染问题
            this.ready();
        });
    },

在 methods 中:

// radar-chart.vue (子组件)
        ready() {
            let vm = this;
            let dom = document.getElementById('radar-chart');

            vm.myChart = echarts.init(dom);
            vm.myChart && vm.myChart.setOption(vm.option);
        },

Demo 页面效果图二: file 这里一共分了三步,引入 ECharts、初始化图表配置、初始化图表,最后可以在 Demo 页面中看到,已经初步地把 ECharts 的雷达图显示到项目中来了。

提取图表配置属性(重点)

上面我们已经成功创建一个雷达图了,但是很明显的是,radar-chart.vue 里的数据写死的,无法重复调用。接下来着手封装的事情了。

封装的思路是这样的:

  1. demo.vue 向 radar-chart.vue 传递一组个性化数据
  2. radar-chart.vue 通过 props 选项接收数据
  3. 提炼接收到的数据,覆盖配置数据 option
  4. 初始化图表

具体实现: 向子组件传递数据,在 data 中定义变量,在 mounted 中赋值

// demo.vue (父组件)
<template>
    <div style="border: 1px solid black; width: 900px; height: 600px; margin: 5px;">
        <radar-chart :indicator="indicator" :legendData="radarData"></radar-chart>
    </div>
</template>

<script>
import radarChart from '@/components/proj-components/echarts/radar-chart';
export default {
    name: 'radar-chart-demo',
    components: {
        radarChart,
    },
    mounted() {
        this.indicator = [
            { name: '销售', max: 6500 },
            { name: '管理', max: 16000 },
            { name: '信息技术', max: 30000 },
            { name: '客服', max: 38000 },
        ];
        this.radarData = [
            {
                value: [4000, 15000, 26000, 21000],
                name: '实际开销(Actual Spending)',
            }
        ];
    },
    data() {
        return {
            indicator: [], // 雷达指示器数据
            legendData: [], // 雷达图例数据
        };
    },
};
</script>

<style scoped>

</style>

在 props 中接收来自父组件的数据

// radar-chart.vue (子组件)

    props: {
        // 指示器数据,必传项
        // 格式举例 [{ name: 'a', max: 1},{ name: 'a', max: 1},{ name: 'a', max: 1}]
        indicator: {
            type: Array,
            default: () => []
        },
        // 图例数据,必填项。
        // 格式举例 [{ value: [5000, 14000, 28000], name: 'name' },{ value: [5000, 14000, 28000], name: 'name' }]
        legendData: {
            type: Array,
            default: () => []
        },
    },

在 ready() 中更新图表数据 option 如果在这里更新 indicator、data 这两个属性值,initOption() 中就不需要初始化这两个值了

// radar-chart.vue (子组件)

    ready() {
            let vm = this;
            let dom = document.getElementById('radar-chart');

            vm.myChart = echarts.init(dom);

            // 得到指示器数据
            vm.option.radar.indicator = vm.indicator;
            // 得到图例数据
            vm.option.series[0].data = vm.legendData;

            vm.myChart && vm.myChart.setOption(vm.option);
        },

Demo 页面效果图三: file

细节优化与其他注意事项

以下代码均是基于已完成代码的修改和添加

1.一个页面有多个图表的情况下,自动生成图表 ID。实现原理参考 ES6 模块化和 .vue组件的应用举例。补充代码:

// radar-chart.vue (子组件)
<template>
    <div :id="chartId" style="height: 100%; width: 100%;"></div>
</template>

<script>
let chartIdSeed = 1;

export default {
    data() {
	    return {
            chartId: 1,
		};
	},
	mounted() {
        let vm = this;
        vm.chartId = 'radar-chart_' + chartIdSeed++;
	},
	methods: {
		let vm = this;
		let dom = document.getElementById(vm.chartId);
	}
};
</script>

2.图表数据属性用 props 接收,图表默认配置属性用 defaultConfig 保存,父组件传入的配置属性 chartConfig 通过 $attrs 直接取得,最终合并为 finallyConfig 使用,利于扩展与维护。补充代码:

// radar-chart.vue (子组件)

<script>
export default {
    data() {
	    return {
            // 默认配置项。以下配置项可以在父组件 :chartConfig 进行配置,会覆盖这里的默认配置
            defaultConfig: {
                tooltipShow: true
			},
            finallyConfig: {}, // 最后配置项
		};
	},
	mounted() {
		// 在这里合并默认配置与父组件传进来的配置
        vm.finallyConfig = Object.assign({}, vm.defaultConfig, vm.$attrs.chartConfig);
	},
	methods: {
		initOption() {
			vm.option = {
                tooltip: {
                    show: vm.finallyConfig.tooltipShow, // 在这里使用最终配置
                },
			}
		},
	}
};
</script>

3.使用 watch 监听图表数据更新

// radar-chart.vue (子组件)

    watch: {
        legendData() {
            this.$nextTick(() => {
                this.ready();
            });
        }
    },

4.添加窗口 resize 事件和图表 click 事件

// radar-chart.vue (子组件)

export default {
    data() {
	    return {
            chartResizeTimer: null, // 定时器,用于resize事件函数节流
		};
	},
	methods: {
		ready() {
            // 添加窗口resize事件
            window.addEventListener('resize', vm.handleChartResize);
			
            // 触发父组件的 @chartClick 事件
            vm.myChart.on('click', function(param) {
                vm.$emit('chartClick', param);
            });
		},
		
		// 处理窗口resize事件
        handleChartResize() {
            let vm = this;
            clearTimeout(vm.chartResizeTimer);
            vm.chartResizeTimer = setTimeout(function() {
                vm.myChart && vm.myChart.resize();
            }, 200);
        },
    },
    beforeDestroy() {
        // 释放该图例资源,较少页面卡顿情况
        if (this.myChart) this.myChart.clear();
        // 移除窗口resize事件
        window.removeEventListener('resize', this.handleChartResize);
    }
};

本文由博客群发一文多发等运营工具平台 OpenWrite 发布


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK