美文网首页vue
做一个可复用的 echarts-vue 组件(延迟动画加载)

做一个可复用的 echarts-vue 组件(延迟动画加载)

作者: 一梦自然醒 | 来源:发表于2018-01-11 17:29 被阅读1276次

    在 vue 项目使用 echarts 的场景中,以下三点不容忽视:1. 可视化的数据往往是异步加载的;2. 若一个页面存在大量的图表( 尤其当存在关系图和地图时 ),往往会导致该页面的渲染速度很慢并可能在几秒内卡死,产生极差的用户体验。3. 引入 echarts 组件导致编译后的文件过大从而使得首次访问的加载极慢。关于第三点,大家可以参考之前的撰文 优化 Vue 项目编译文件大小。以下针对上述前两点,给出数据异步、延迟渲染的 echarts vue 组件的设计和实现方式,并对实现之中可能存在的问题进行介绍。

    1. 抽离 echarts 公共部分形成基础组件


    1.1 调研公共部分

    首先,我们需要把 echarts 使用中公共的部分抽离出来,形成基础组件。

    让我们在 官网 - 5 分钟上手 ECharts 教程中找到使用 echarts 的步骤:

    # 1. 获取一个用于挂在 echarts 的 DOM 元素
    let $echartsDOM = document.getElementById('echarts-dom')
    
    # 2. 初始化
    let myEcharts = echarts.init($echartsDOM)
    
    # 3. 设置配置项
    let option = {...}
    
    # 4. 为 echarts 指定配置
    myEcharts.setOption(option)
    
    注:在 Vue 中,首先我们需要使用 import echarts from 'echarts' 以引入 echarts。

    由上可知,在 echarts 使用中,除第三步设置配置项以外,其他的步骤都是重复的,即可以抽离出来放入组件中统一实现。

    注:其实 option 配置中也存在可以抽离的部分,比如我们可以将 echarts 的颜色、散点大小、折线粗细等提取出来统一赋值,以保证 echarts 风格的统一。但由于不同类型的 ehcarts 图的颜色配置方式不同,因而实现起来相对繁琐,这里不进行说明,有兴趣的同学可以自行尝试。

    1.2 实现 echarts 功能

    首先我们书写一个简单 ehcart.vue,其中,配置项直接复制于官网的教程示例。

    <style scoped>
        .echarts {
            width: 100%;
            height: 100%;
        }
    </style>
    
    <template>
        <div>
            <div class="echarts" id="echarts-dom"></div>
        </div>
    </template>
    
    <script>
        import echarts from 'echarts'
    
         export default {
            name: 'echarts',
            data() {
                return {}
            },
            mounted() {
                let $echartsDOM = document.getElementById('echarts-dom')
                let myEcharts = echarts.init($echartsDOM)
                let option = {
                    title: {
                        text: 'ECharts 入门示例'
                    },
                    tooltip: {},
                    legend: {
                        data: ['销量']
                    },
                    xAxis: {
                        data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
                    },
                    yAxis: {},
                    series: [{
                        name: '销量',
                        type: 'bar',
                        data: [5, 20, 36, 10, 10, 20]
                    }]
                }
                myEcharts.setOption(option)
            }
        }
    </script>
    

    然后在App.vue中引入这一组件,并设置 echarts 的宽高:

    <style>
        .echarts-container{
            width: 100%;
            height: 20rem;
        }
    </style>
    
    <template>
        <div id="app">
            <i-echart class="echarts-container"></i-echart>
        </div>
    </template>
    
    <script>
        import iEchart from './components/echart'
    
        export default {
            name: 'app',
            components: {
                iEchart
            }
        }
    </script>
    

    刷新页面后,即可看到柱状图。

    1.3 组件化

    由于我们需要抽离 option 部分,最好的方式是将其作为组件的属性,即 props 交由调用方配置:

    # echart.vue
    
    import echarts from 'echarts'
    
    export default {
        name: 'echarts',
        props: {
            option: {
                type: Object,
                default(){
                    return {}
                }
            }
        },
        data() {
            return {}
        },
        mounted() {
            let $echartsDOM = document.getElementById('echarts-dom')
            let myEcharts = echarts.init($echartsDOM)
            let option = this.option
            myEcharts.setOption(option)
        }
    }
    

    1.4 调用组件

    然后我们可以将 option 配置抽离到组件调用方,并通过「传参」的方式进行调用:

    <i-echart :option="option" class="echarts-container"></i-echart>
    

    1.5 提高组件强壮型

    之前我们注意到,在 option 参数中,我们给出了默认值 {},即空对象。这样做其实是有问题的,即在 echarts 中,如果传入的 option 配置对象不含有 series 键,就会抛出错误:

    Error: Option should contains series.
    

    默认值处理是需要存在的,即当调用方传入的对象为空或不存在 series 配置时,应在页面上显示一些提示( 对用户友好的提示,而不是对编程人员 ),即避免因报错而造成空白的情况。

    此外,当我们像之前那样给 option 这一参数进行类型限制后,倘若调用方传入非对象类型,Vue 会直接抛出错误——这一结果也不是我们想要的。我们应该取消类型限制,并在 option 发生变化时进行依次以下判断:

    1. 是否为对象;
    2. 是否为空对象;
    3. 是否包含 series 键;
    4. series 是否为数组;
    5. series 数组是否为空。
    

    代码实现如下:

    function isValidOption(option){
        return isObject(option) && !isEmptyObject(option)
                && hasSeriesKey(option)
                && isSeriesArray(option) && !isSeriesEmpty(option)
    }
    
    function isObject(option) {
        return Object.prototype.isPrototypeOf(option)
    }
    
    function isEmptyObject(option){
        return Object.keys(option).length === 0
    }
    
    function hasSeriesKey(option){
        return !!option['series']
    }
    
    function isSeriesArray(option) {
        return Array.isArray(option['series'])
    }
    
    function isSeriesEmpty(option){
        return option['series'].length === 0 
    }
    
    注:实际上,当判断出 option 为对象后,可以直接进行第三步的判断。

    然后,当判断 option 符合上述三种情况时,在页面上显示如「数据为空」之类的提示:

    import echarts from 'echarts'
    
    export default {
        name: 'echarts',
        props: {
            option: {
                default(){
                    return {}
                }
            }
        },
        data() {
            return { }
        },
        mounted() {
            //# 1. 获取一个用于挂在 echarts 的 DOM 元素
            let $echartsDOM = document.getElementById('echarts-dom')
            //# 2. 初始化
            let myEcharts = echarts.init($echartsDOM)
            //# 3. 设置配置项    let option = {...}
           //# 4. 为 echarts 指定配置   myEcharts.setOption(option)
            this.myEcharts = myEcharts
            this.checkAndSetOption()
        },
        watch: {
            option(option){
                this.checkAndSetOption()
            }
        },
        methods: {
            checkAndSetOption(){
                let option = this.option    //配置等于父组件传过来的数据
                if(isValidOption(option)){
                    this.myEcharts.setOption(option);       //渲染出来
                    this.myEcharts.hideLoading();           //隐藏加载动画
                }else{
                    this.myEcharts.showLoading();           //加载动画
                }
            }
        }
    }
    

    这里在书写代码时,有以下几点需要注意:

    • 1、我们对 DOM 元素获取结果做了校验,即当 option 不符合要求时,ID 为 echarts-dom 的 DOM 元素是不存在的,此时 document.getElementById() 的返回结果为空,不能直接使用 echarts.init(),否则会抛出错误:Error: Initialize failed: invalid dom
    • 2、在 Vue 中,初始化的值不会被 watch 钩子捕捉,从而导致组件被调用方调用并赋予 option 参数时不会进入校验。虽然可以使用 immediate: true 使得 watch 钩子能够在属性初始化赋值时被触发,但这样做是不合适的。因为这样设置之后,在 option 初始化从而触发 watch 时,用于挂载 echarts 的 DOM 元素还未存在于页面中,从而导致出现 TypeError: Cannot read property 'setOption' of null 的错误。我们要重点注意 echarts 作用的生命周期,这一点后续还会涉及。

    1.6 增强组件功能 - 数据加载提示

    在实际场景中,用于渲染的数据常常是异步获取的,在异步加载数据之中,我们可能需要在页面中显示如「正在加载...」的字样来表示加载过程正在进行以提高用户体验。而加载过程就组件而言是无法直接获取的,所以,我们需要使用某一参数用于进行加载信息的显示

    ECharts 默认有提供了一个简单的加载动画。只需要调用 showLoading 方法显示。数据加载完成后再调用 hideLoading 方法隐藏加载动画。

    //在App.vue中模拟3秒后获取数据
    data() {
      return {
        option:  {}
      }
    },
    created(){
        setTimeout(()=>{
            this.option={
                          title: {
            text: 'ECharts 入门示例'
          },
          tooltip: {},
          legend: {
            data: ['销量']
          },
          xAxis: {
            data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
          },
          yAxis: {},
          series: [{
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }]
            }
            console.log(this.option);
        },3000)
    }
    

    然后就可以在echarts组件里调用了

    methods: {
        checkAndSetOption(){
            let option = this.option    //配置等于父组件传过来的数据
            if(isValidOption(option)){
                this.myEcharts.setOption(option);       //渲染出来
                this.myEcharts.hideLoading();           //隐藏加载动画
            }else{
                this.myEcharts.showLoading();           //加载动画
            }
        }
    }
    

    1.7 增强组件功能 - 数据不合法提示

    当传入的 option 值不符合规定时。基于这一标识,我们可以对 echarts 组件进行优化,当 option 不合法或数据为空时给出提示信息而不是显示空白甚至报错。

    PS:暂时更新到这了,如果有后续,我回接着更新了

    相关文章

      网友评论

        本文标题:做一个可复用的 echarts-vue 组件(延迟动画加载)

        本文链接:https://www.haomeiwen.com/subject/ddhtoxtx.html