vue之将echart封装为组件

最近的新项目里,有大量数据图表类的需求,为了增强代码的复用性,减少冗余,我开始思考如何将echart封装为组件调用。本文将会以雷达图为案例,一步步讲解在vue项目中如何使用echart,如何将其封装为能重复调用的组件。

阅读之前,可以先看看我之前的一些文章,这样能更好的理解这次的内容:
【使用vue-cli(vue脚手架)快速搭建项目】:https://www.jianshu.com/p/1ee1c410dc67
【vue之父子组件间通信实例讲解(props、$ref 、 $emit )】:
https://www.jianshu.com/p/91416e11f012

1. 安装Echarts

首先第一步,肯定是安装Echarts了。通过cd命令进入项目根目录,然后敲以下命令行:

cnpm install echarts -S

安装成功会如下显示,package.json里的dependencies属性也会自动加上Echarts依赖:


安装Echarts

package.json

2.在vue项目中使用Echarts

安装成功以后,我们先要考虑的是如何在vue项目中导入Echarts,并成功初始化一个图表。

下面我会先建两个.vue文件 chart.vue 和 radar-chart.vue 作为这次示例的基础。 chart.vue的角色是调用雷达图,radar-chart.vue的角色是提供雷达图:

//chart.vue 

<template>
  <radar-chart></radar-chart>
</template>

<script>
  import RadarChart from '../components/radar-chart'
  export default {
    name: "chart",
    components: {RadarChart},
    component: RadarChart
  }
</script>
//radar-chart.vue

<template>

</template>

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

好,正式创建一个Echarts图表了
(1)在radar-chart.vue导入echart :
  // 引入基本模板
  import echarts from 'echarts/lib/echarts'
  // 引入雷达图组件
  import 'echarts/lib/chart/radar'
  // 引入提示框和图例组件
  import 'echarts/lib/component/tooltip'
  import 'echarts/lib/component/legend'
(2)创建图表配置数据,数据格式参考 Echarts官网:
      const option = {
        tooltip: {},
        radar: {
          indicator: [{name: '体育', max: '100'}, {name: '数学', max: '100'}, {name: '化学', max: '100'}, {name: '劳动',  max: '100'}, {name: '物理',  max: '100'}],
          center: ['50%', '51%']
        },
        series: [{
          type: 'radar',
          itemStyle: {normal: {areaStyle: {type: 'default'}}},
          data: [
            {
              value: [58,56,78,64,98],
              name: '各项得分',
              itemStyle: {normal: {color: '#f0ad4e'}}
            }
          ]
        }]
      }
(3)初始化图表:
   const chartObj = echarts.init(document.getElementById('radar'))
   chartObj.setOption(option)

上面几步汇总为以下代码,另外补充一点就是,创建配置数据option和初始化的时候,都要放在mounted钩子函数里执行,这样才能保证获取dom的是时候,dom已完成渲染:

//chart.vue 

<template>
  <radar-chart></radar-chart>
</template>

<script>
  import RadarChart from '../components/radar-chart'
  export default {
    name: "chart",
    components: {RadarChart},
    component: RadarChart
  }
</script>
//radar-chart.vue
<template>
  <div id="radar" class="container"> </div>
</template>

<script>
  // 引入基本模板
  import echarts from 'echarts/lib/echarts'
  // 引入雷达图组件
  import 'echarts/lib/chart/radar'
  // 引入提示框和图例组件
  import 'echarts/lib/component/tooltip'
  import 'echarts/lib/component/legend'
  export default {
    name: "radar-chart",
    mounted(){
      const option = {  //创建图表配置数据
        tooltip: {},
        radar: {
          indicator: [{name: '体育', max: '100'}, {name: '数学', max: '100'}, {name: '化学', max: '100'}, {name: '劳动',  max: '100'}, {name: '物理',  max: '100'}],
          center: ['50%', '51%']
        },
        series: [{
          type: 'radar',
          itemStyle: {normal: {areaStyle: {type: 'default'}}},
          data: [
            {
              value: [58,56,78,64,98],
              name: '各项得分',
              itemStyle: {normal: {color: '#f0ad4e'}}
            }
          ]
        }]
      }
     //初始化图表
      const chartObj = echarts.init(document.getElementById('radar'))
      chartObj.setOption(option)
    }
  }
</script>
<style scoped>
  .container{width: 500px;height: 400px;}
</style>

出来的效果是这样的:


效果一

3.将Echarts封装为组件

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

封装的思路是这样的:
(1)chart.vue向radar-chart.vue传递一组个性化数据
(2)radar-chart.vue通过props选项接收数据
(3)提炼接收到的数据,覆盖配置数据option
(4)初始化图表
(如果对如何传递数据不理解,可以先看看我在开始的时候提到的,我的另一篇文章 —— “ vue之父子组件间通信实例讲解(props、$ref 、 $emit )”)

具体代码如下:

//chart.vue (父组件)

<template>
  <radar-chart :items="items"></radar-chart> //传递在子组件prop选项里约定好的数据
</template>

<script>
  import RadarChart from '../components/radar-chart'
  export default {
    name: "chart",
    components: {RadarChart},
    component: RadarChart,
    data () {
      return {
        items: [{name: '体育', value: 95, max: '100'}, {name: '数学', value: 55, max: '100'}, {name: '化学', value: 75, max: '100'}, {name: '劳动', value: 85, max: '100'}, {name: '烹饪', value: 85, max: '100'}]
      }
    }
  }
</script>
//radar-chart.vue (子组件)

<template>
  <div id="radar" class="container"> </div>
</template>

<script>
  // 引入基本模板
  import echarts from 'echarts/lib/echarts'
  // 引入雷达图组件
  import 'echarts/lib/chart/radar'
  // 引入提示框和图例组件
  import 'echarts/lib/component/tooltip'
  import 'echarts/lib/component/legend'
  export default {
    name: "radar-chart",
    props: {                //接受父组件传递来的数据
      items: {
        type: Array,
        default () {        //默认数据,没有数剧的情况下启用
          return [{name: '生物', value: 95, max: '100'}, {name: '数学', value: 55, max: '100'}, {name: '语文', value: 86, max: '100'}, {name: '物理', value: 54, max: '100'}, {name: '美术', value: 59, max: '100'}]
        }
      },
    },
    mounted(){
      let values = []  //提炼接收到的数据
      this.items.forEach(el => {
        values.push(el.value)   
      })                       
      const option = { //覆盖配置数据option
        tooltip: {},
        radar: {
          indicator: this.items,  
          center: ['50%', '51%']
        },
        series: [{
          type: 'radar',
          itemStyle: {normal: {areaStyle: {type: 'default'}}},
          data: [
            {
              value: values,  
              name: '各项得分',
              itemStyle: {normal: {color: '#f0ad4e'}}
            }
          ]
        }]
      }
      //初始化
      const chartObj = echarts.init(document.getElementById('radar'))
      chartObj.setOption(option)
    }
  }
</script>
<style scoped>
  .container{width: 500px;height: 400px;}
</style>

封装以后,就能传递自定义的数据,反复调用了。上面代码最后的效果是这样的:


效果二

4.细节优化

基本的功能已经实现了,下面我们来优化下一些细节。

不知道大家有没发现radar-chart.vue里的<template>-id是写死的,这会出现什么问题?当一个页面调用两次这个雷达图组件,id就会重复了,从而报错。

为了解决这个问题,我引入了uuid(vue-cli项目自带,不需另外安装),意在为每个生成的雷达图配一个不重复的随机id。还需要注意的是,我们要在created()方法里去做这个生成id的事情,如果写在mounted了就会出现无法初始化的情况,因为来不及渲染新的ID,就执行document.getElementById()了,具体代码如下:

<template>
  <div>
    <radar-chart :items="items_one"></radar-chart>
    <radar-chart :items="items_two"></radar-chart>
  </div>
</template>

<script>
  import RadarChart from '../components/radar-chart'
  export default {
    name: "chart",
    components: {RadarChart},
    component: RadarChart,
    data () {
      return {
        items_one: [{name: '体育', value: 95, max: '100'}, {name: '数学', value: 55, max: '100'}, {name: '化学', value: 75, max: '100'}, {name: '劳动', value: 85, max: '100'}, {name: '烹饪', value: 85, max: '100'}],
        items_two: [{name: '体育', value: 22, max: '100'}, {name: '数学', value: 55, max: '100'}, {name: '化学', value: 75, max: '100'}, {name: '劳动', value: 85, max: '100'}, {name: '烹饪', value: 85, max: '100'}]
      }
    }
  }
</script>

<template>
  <div :id="elId" class="container"> </div>
</template>

<script>
  // 引入基本模板
  import echarts from 'echarts/lib/echarts'
  // 引入雷达图组件
  import 'echarts/lib/chart/radar'
  // 引入提示框和图例组件
  import 'echarts/lib/component/tooltip'
  import 'echarts/lib/component/legend'
  //引入uuid文件
  import uuidv1 from 'uuid/v1' 
  export default {
    name: "radar-chart",
    props: {
      items: {
        type: Array,
        default () {
          return [{name: '生物', value: 95, max: '100'}, {name: '数学', value: 55, max: '100'}, {name: '语文', value: 86, max: '100'}, {name: '物理', value: 54, max: '100'}, {name: '美术', value: 59, max: '100'}]
        }
      },
    },
    data () {
      return {
        elId: ''
      }
    },
    created(){
      this.elId = uuidv1() //获取随机id
    },
    mounted(){
      let values = []
      this.items.forEach(el => {
        values.push(el.value)
      })
      const option = {
        tooltip: {},
        radar: {
          indicator: this.items,
          center: ['50%', '51%']
        },
        series: [{
          type: 'radar',
          itemStyle: {normal: {areaStyle: {type: 'default'}}},
          data: [
            {
              value: values,
              name: '各项得分',
              itemStyle: {normal: {color: '#f0ad4e'}}
            }
          ]
        }]
      }
      const chartObj = echarts.init(document.getElementById(this.elId));
      chartObj.setOption(option)
    }
  }
</script>
<style scoped>
  .container{width: 500px;height: 400px;}
</style>

成功解决重复id的问题:


效果三

整个封装echart的思路就是上面那样了,其实这个demo还不完美,像雷达图的宽高也是写死的,还没来得及做更多的优化,后面有空慢慢再完善它~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容