在大数据盛行的今天,数据可视化变得越来越广泛。而在前端工作中,数据可视化用得最多的,可能就是图表了。在众多的图表插件中,echarts以其良好的性能和完善的API,图表的多样性和功能的完整性,被广大开发者认可,成为了前端图表使用最多的工具,所以,今天我就简单的讲一下,如何在vue中更优雅的使用echarts。

第一步,下载echarts我就不多说了:npm install echarts.

然后我们看一下一般人(刚开始用vue或echarts)的使用方法,大概是这个样子:

<template>    <div>        <div ref="chartColumn" style="width:100%; height:400px;"></div>        <button @click="changeOption">点击改变内容</button>    </div></template> <script>    import echarts from 'echarts'    export default {        data() {            return {                chartColumn: null,                option:{                    title: {                         text: '普通图表'                    },                    tooltip: {},                    xAxis: {                        data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]                    },                    yAxis: {},                    series: [{                        name: '销量',                        type: 'bar',                        data: [5, 20, 36, 10, 10, 20]                    }]                },                data:[5, 20, 36, 10, 10, 20,5, 20, 36, 10, 20, 36, 10, 10, 20,5, 20, 36, 10]            }        },        methods: {            changeOption(){                var r = Math.floor(Math.random()*12);                //splice会改变原来的数组                //var data = this.data.splice(r,6);                var d = this.data.slice(r,r+6);                this.option.series[0].data = d;                this.columnChart.setOption(this.option);            },                        initChart() {                this.chartColumn = echarts.init(this.$refs.chartColumn);                this.chartColumn.setOption(this.option);            }        },        mounted: function () {            this.initChart()        }    }</script>复制代码

上面的代码,虽然功能实现了,也没什么错误,但是需要优化的地方很多。


首先,我们考虑到多个地方需要用到echarts,封装一个组件出来,就像下面这样:

组件部分:

// components/chart-block.vue <template>    <div ref="chartEl" style="height:100%"></div></template><script>    import echarts from 'echarts';    export default {        data(){            return {                chart:null            }        },        props:{            option:{                type:Object            }        },        watch:{            option:{                handler(newValue, oldValue) {                    this.chart.setOption(newValue);                },                // 因为option是个对象,而我们对于echarts的配置项,要更改的数据往往不在一级属性里面                // 所以这里设置了deep:true,不知道该属性的可以看一下vue的文档                deep: true            }        },        mounted(){            this.chart = echarts.init(this.$refs.chartEl);            this.chart.setOption(this.option);        }    }</script>复制代码

然后使用它:

<template>    <div>        <div id="chartColumn" style="width:100%; height:400px;">            <chart-lock :option="option"></chart-lock>        </div>        <button @click="changeOption">点击改变内容</button>    </div></template> <script>    import chartBlock from '@/components/chart-block.vue'    export default {        components:{            chartBlock        },        data() {            return {                option:{                    //... 省略其他属性                    series: [{                        name: '销量',                        type: 'bar',                        data: [5, 20, 36, 10, 10, 20]                    }]                },                data:[5, 20, 36, 10, 10, 20,5, 20, 36, 10, 20, 36, 10, 10, 20,5, 20, 36, 10]            }        },        methods: {            changeOption(){                var r = Math.floor(Math.random()*12);                //splice会改变原来的数组                //var data = this.data.splice(r,6);                var d = this.data.slice(r,r+6);                this.option.series[0].data = d;            }        }    }</script>复制代码

和之前的比起来,我们看看封装后的组件做了什么:

1、不用给每个图表指定ref属性了

2、不用定义图表变量了

(1和2在我们同一个页面有多个图表时,减少了很多不必要的变量)

3、不用自己初始化图表了

4、数据改变时,我们自动监听,再也不需要手动处理了

(3和4在我们同一个页面有多个图表时,减少了重复逻辑的工作量)


上面针对的,是同一个页面有多个图表时的优化。接下来,我们继续优化,如果一个组件有多个页面都要用到,我们就该考虑把它注册为全局的,减少引用。代码如下:

// components/chart-block.vue <template>    <div ref="chartEl"></div></template><script>    import echarts from 'echarts';    var ChartBlock = {        name:'ChartBlock', // 这里加个name属性,用于注册使用        ... //其他的属性    }        /* 注册组件的方法 */    ChartBlock.install = function(Vue) {        Vue.component(ChartBlock.name, ChartBlock);    };    export default ChartBlock;</script>复制代码

main.js全局注册

import ChartBlock from '@/components/chart-block.vue'Vue.use(ChartBlock)复制代码

在需要使用组件的页面,下面的代码就不需要了

//import chartBlock from '@/components/chart-block.vue' export default {    components:{        ...        //chartBlock    }    ...}复制代码

好了,到这里已经解决了组件的复用性,以后再也不用引入和声明组件了。接下来再做点其他的。很多时候,我们的图表可能都需要跟随窗口进行实时的动态改变,每个组件都单独写,显然不现实,那我们最好的办法,就是把跟随窗口改变的代码直接写在组件里面,需要注意的是,一定要在组件销毁时移除窗口改变的监听。继续完善我们的组件,如下:

var ChartBlock = {        name:'ChartBlock',        data(){            return {                chart:null            }        },        props:{            option:{                type:Object            }        },        methods:{            returnChartToParent(){                this.$emit('chartReady',this.chart);            }        },        watch:{            option:{                handler(newValue, oldValue) {                    this.chart.setOption(newValue);                },                deep: true            }        },        mounted(){            this.chart = echarts.init(document.getElementById(this.id));            this.chart.setOption(this.option);                        // 添加窗口改变时的监听,跟随实时窗口改变            var chart = this.chart;            this.chart.__resize = function(){                chart.resize();            };            setTimeout(() => {                window.addEventListener('resize',this.chart.__resize);            }, 200);        },        beforeDestroy() {            // 移除窗口改变监听            window.removeEventListener('resize',this.chart.__resize);        }    }复制代码

有了上面的补充,以后再也不用担心图表响应的问题了,只管调用即可。但是,这样就OK了吗?作为一个好(bushitailan)的开发,我们要的可不只是功能,还要考虑性能的问题啊。

第一点:像窗口改变大小这种事件,一旦存在拖动,将发生得太频繁,我们很有必要做一下节流处理(不知道函数节流的,自行了解一下,算是前端必须掌握的基础技能哦)。解决办法,自然就是添加一个节流函数,继续看代码,主要改变的就是mounted函数,其他不变。

var ChartBlock = {        name:'ChartBlock',        ...        mounted(){ // 添加函数节流处理            this.chart = echarts.init(document.getElementById(this.id));            this.chart.setOption(this.option);            // 节流函数,来自Lodash,这里可以自己写一个简单点的            // 如果有多个地方用到,也可以使用引入的方式            function throttle(func, wait, options) {                let time, context, args, result;                let previous = 0;                if (!options) options = {};                 let later = function() {                    previous = options.leading === false ? 0 : new Date().getTime();                    time = null;                    func.apply(context, args);                    if (!time) context = args = null;                };                 let throttled = function() {                    let now = new Date().getTime();                    if (!previous && options.leading === false) previous = now;                    let remaining = wait - (now - previous);                    context = this;                    args = arguments;                    if (remaining <= 0 || remaining > wait) {                    if (time) {                        clearTimeout(time);                        time = null;                    }                    previous = now;                    func.apply(context, args);                    if (!time) context = args = null;                    } else if (!time && options.trailing !== false) {                        time = setTimeout(later, remaining);                    }                };                return throttled;            };             var chart = this.chart;            this.chart.__resize = throttle(function(){                chart.resize();            },200);             setTimeout(() => {                window.addEventListener('resize',this.chart.__resize);            }, 200);        },        beforeDestroy() {            window.removeEventListener('resize',this.chart.__resize);        }    }复制代码

第二点:也是更重要的一点,从vue 的角度出发,我们把图表的 option 写在 data 里,是很浪费性能的。为什么这么说呢,因为 vue 的数据改变监听实质上就是对 data 对象进行逐层循环,为每一个属性添加监听。而我们图表的数据对象,只是我们图表需要的一些配置项,压根不参与业务逻辑,一个简单的对象还好,但复杂的 echarts 图表,一个option 上100个属性,如果一个页面再有多个图表的话,得额外添加多少没用的监听器,可想而知。所以,我的建议是,把 option 数据写到 data 之外,然后通过调用 echarts 的 setOption 方法设置数据。既然如此,那我们就得在chart-block组件里暴露出我们的setOption方法,以供父组件调用了。继续修改代码:

<template>    <div ref="chartEl" style="height:100%;"></div></template><script>    import echarts from 'echarts';     var ChartBlock = {        name:'ChartBlock',        data(){            return {                chart:null            }        },        // 去除props和watch,添加methods        methods:{            setOption(option){                this.chart && this.chart.setOption(option)            }        },        mounted(){            ... //同上        },        beforeDestroy() {            window.removeEventListener('resize',this.chart.__resize);        }    }        /* 注册方法 */    ChartBlock.install = function(Vue) {        Vue.component(ChartBlock.name, ChartBlock);    };    export default ChartBlock;</script>复制代码

使用如下:

<template>    <div>        <div id="chartColumn" style="width:100%; height:400px;">            <!--少了option,多了ref,用于调用setOption方法-->            <chart-lock ref="chart1"></chart-lock>        </div>        <button @click="changeOption">点击改变内容</button>    </div></template> <script>    import chartBlock from '@/components/chart-block.vue'    // echarts图表的配置数据写在外面,而不是data里    let option1 = {        //... 省略其他属性        series: [{            name: '销量',            type: 'bar',            data: [5, 20, 36, 10, 10, 20]        }]    };    let data = [5, 20, 36, 10, 10, 20,5, 20, 36, 10, 20, 36, 10, 10, 20,5, 20, 36, 10]    export default {        components:{            chartBlock        },        data() {            return {             }        },        methods: {            changeOption(){                var r = Math.floor(Math.random()*12);                var d = data.slice(r,r+6);                option1.series[0].data = d;                this.$refs.chart1.setOption(option1);            }        },        mounted(){            this.$refs.chart1.setOption(option1);        }    }</script>复制代码


这样一来,通过一个组件,我们就把echarts图表的复用性,响应性(还自带节流处理),以及数据分离都解决了,是不是跟一开始的代码比起来,优雅了很多呢。

好了,本文到此结束。文章如有写得不对或不足的地方,欢迎指出。如果有什么不明白的,可以通过留言提问。

最后,站在产品的角度说句话,我们可以给图表添加个 type 的 props 属性,在数据没拿到之前,根据type,展示不同的默认图,这样是不是比空白好了很多呢?

原文地址:liguixing.com/archives/10…


Logo

前往低代码交流专区

更多推荐