Vue 2.x折腾记 - (7) 写一个挺不靠谱的Vue-Echarts组件

简介: 上基友社区看了下,发现对echarts的封装都是打包进去的,想想就还是算了。图表这货.说实在的,若不是整个系统大量用到,打包进去没必要。CDN是个好东西,我们完全可以写一个异步加载JS然后封装按需调用。至于你能学到什么,见仁见智了,若有所收获就是我这文章的意义所在了 。


前言


上基友社区看了下,发现对echarts的封装都是打包进去的,想想就还是算了。


图表这货.说实在的,若不是整个系统大量用到,打包进去没必要。


CDN是个好东西,我们完全可以写一个异步加载JS然后封装按需调用。


至于你能学到什么,见仁见智了,若有所收获就是我这文章的意义所在了 。


效果图



实现思路及实现的功能


  • 异步引入echarts,有三个版本,不是最大也不是最小那个,具体上官网看
  • 图表的销毁释放内存
  • 图表跟随父包含块自适应(事件监听)
  • setOption的抽离,图表实例化id随机生成或者手动传入
  • setOption可以一次性传入整个图表的参数,也可以拆分传入(太多,只选了几个复用率很高选项的)...都设置了默认值.
  • 随机id是大写字母的组合,外部有传入则用外部的


代码


实现Vue.use(?)


index.js -- 导出组件的,内部实在亮瞎眼


import echarts from "./echarts";
// Vue.use必须有install这个函数..可能我这里写的比较粗糙..有兴趣的可以去完善
// 用是可以正常使用的
export default {
  install: function(Vue, Option) {
    Vue.component("vue-echarts", echarts);
  }
};


echarts.vue


<template>
  <div class="vue-echarts" :id="id" :style="style">
  </div>
</template>
<script>
  export default {
    name: 'vue-echarts',
    data: function () {
      return {
        loadJS: '', // 存储异步加载echart的promise状态
        chart: '', // 保存地图初始化的实例
      }
    },
    props: {
      id: {
        type: String,
        default: function () { // 生成一个随机字符串,纯英文的,当不传入ID的时候生成实例,加i确保不会随机到一样的
          let temp = [];
          for (let i = 0; i < 6; i++) {
            let randomChar = String.fromCharCode(65 + Math.floor(Math.random() * 19) + i);
            temp.push(randomChar);
          }
          return temp.reduce((pre, next) => pre + next);
        }
      },
      height: { // 图表高度
        type: String,
        default: '300px'
      },
      width: { // 图表宽度
        type: String,
        default: '100%'
      },
      legend: { // 以下的配置都是echarts官方图表的自定义部分,我拆分这么几大块
        type: Object,
        default: function () {
          return {
            data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
          }
        }
      },
      title: {
        type: Object,
        default: function () {
          return {
            text: '堆叠区域图'
          }
        }
      },
      tooltip: {
        type: [Array, Object],
        default: function () {
          return {
            trigger: 'axis',
            axisPointer: {
              type: 'cross',
              label: {
                backgroundColor: '#6a7985'
              },
              animation: true
            }
          }
        }
      },
      toolbox: {
        type: Object,
        default: function () {
          return {
            show: true,
            feature: {
              dataZoom: {
                yAxisIndex: 'none'
              },
              dataView: { readOnly: false },
              magicType: { type: ['line', 'bar'] },
              restore: {},
              saveAsImage: {}
            }
          }
        }
      },
      grid: {
        type: Object,
        default: function () {
          return {
            left: '3%',
            right: '4%',
            bottom: '3%',
            containLabel: true
          }
        }
      },
      xAxis: {
        type: [Array, Object],
        default: function () {
          return [
            {
              type: 'category',
              boundaryGap: true,
              data: ['00:00', '02:00', '04:00', '06:00', '08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00', '24:00']
            }
          ]
        }
      },
      yAxis: {
        type: [Array, Object],
        default: function () {
          return [
            {
              type: 'value'
            }
          ]
        }
      },
      series: {
        type: [Array, Object],
        default: function () {
          return [
            {
              name: '曝光',
              type: 'line',
              smooth: true,
              lineStyle: {
                normal: {
                  color: '#f00',
                }
              },
              data: [120, 132, 101, 134, 90, 230, 210, 120, 132, 101, 134, 90, 120]
            },
            {
              name: '点击',
              type: 'line',
              smooth: true,
              lineStyle: {
                normal: {
                  color: '#20a0ff',
                }
              },
              data: [220, 182, 191, 234, 290, 330, 310, 120, 132, 101, 134, 90, 120]
            },
            {
              name: '点击率',
              type: 'line',
              lineStyle: {
                normal: {
                  color: '#42b983',
                }
              },
              smooth: true,
              data: [150, 232, 201, 154, 190, 330, 410, 120, 132, 101, 134, 90, 120]
            }
          ]
        }
      },
      setOption: {
        type: Object
      },
      dispose: Boolean
    },
    computed: {
      style () {
        return {
          height: this.height,
          width: this.width
        }
      }
    },
    created () {
      this.loadJS = this.loadEchartsJS();
    },
    mounted () {
      this.loadJS.then(() => {
        let st = setTimeout(() => {
           this.init(); // CDNJS加载后,元素渲染后,初始化图表
        }, 300);
      }).catch(err => {
        console.log('echarts js加载失败');
      });
    },
    beforeDestroy () {
      window.removeEventListener('resize', this.chart.resize)
      if (this.chart) {
        this.chart.dispose(); // 销毁图表实例
      }
    },
    methods: {
      init () { // 初始化图表
        this.chart = new echarts.init(document.getElementById(this.id));
        this.chart.setOption(this.setOption);
        window.addEventListener('resize', this.chart.resize) // 图表响应大小的关键,重绘
      },
      loadEchartsJS () { // 异步请求cdnjs
        const CDNURL = 'https://cdn.bootcss.com/echarts/3.7.1/echarts.min.js';
        return new Promise((resolve, reject) => {
          const script = document.createElement('script');
          script.type = "text/javascript";
          script.id = "cdnEchart";
          if (script.readyState) {  //IE
            script.onreadystatechange = function () {
              if (script.readyState == "loaded" ||
                script.readyState == "complete") {
                script.onreadystatechange = null;
                resolve('success: ' + CDNURL);
              }
            };
          } else {  //Others
            script.onload = function () {
              resolve('success: ' + CDNURL);
            };
          }
          script.onerror = function () {
            reject(Error(CDNURL + 'load error!'));
          };
          script.src = CDNURL;
          if (!document.getElementById('cdnEchart')) {
            document.head.appendChild(script);
          }
        });
      }
    },
    watch: {
      setOption: {
        handler: function (newVal, oldVal) { // 监听外部传入的值,渲染新的的图表数据
          if (this.chart) {
            if (newVal) {
              this.chart.setOption(newVal);
            } else {
              this.chart.setOption(oldVal);
            }
            this.chart.resize();
          } else {
            setTimeout(() => {
              this.init();
            }, 300);
          }
        },
        deep: true
      }
    }
  }
</script>


如何使用


  • main.js -- 主入口文件,


// promise的polyfill
require("es6-promise").polyfill(); 
// 百度图表
import echarts from './components/common/echarts';
Vue.use(echarts);
目录
相关文章
|
3月前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
330 2
|
6月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
818 0
|
8月前
|
JavaScript
vue实现任务周期cron表达式选择组件
vue实现任务周期cron表达式选择组件
1053 4
|
6月前
|
JavaScript UED
用组件懒加载优化Vue应用性能
用组件懒加载优化Vue应用性能
|
6月前
|
JavaScript 前端开发 UED
Vue 表情包输入组件的实现代码:支持自定义表情库、快捷键发送和输入框联动的聊天表情解决方案
本文详细介绍了在 Vue 项目中实现一个功能完善、交互友好的表情包输入组件的方法,并提供了具体的应用实例。组件设计包含表情分类展示、响应式布局、与输入框的交互及样式定制等功能。通过核心技术实现,如将表情插入输入框光标位置和点击外部关闭选择器,确保用户体验流畅。同时探讨了性能优化策略,如懒加载和虚拟滚动,以及扩展性方案,如自定义主题和国际化支持。最终,展示了如何在聊天界面中集成该组件,为用户提供丰富的表情输入体验。
519 8
|
6月前
|
JavaScript 前端开发 UED
Vue 表情包输入组件实现代码及详细开发流程解析
这是一篇关于 Vue 表情包输入组件的使用方法与封装指南的文章。通过安装依赖、全局注册和局部使用,可以快速集成表情包功能到 Vue 项目中。文章还详细介绍了组件的封装实现、高级配置(如自定义表情列表、主题定制、动画效果和懒加载)以及完整集成示例。开发者可根据需求扩展功能,例如 GIF 搜索或自定义表情上传,提升用户体验。资源链接提供进一步学习材料。
292 1
|
8月前
|
存储 JavaScript 前端开发
基于 ant-design-vue 和 Vue 3 封装的功能强大的表格组件
VTable 是一个基于 ant-design-vue 和 Vue 3 的多功能表格组件,支持列自定义、排序、本地化存储、行选择等功能。它继承了 Ant-Design-Vue Table 的所有特性并加以扩展,提供开箱即用的高性能体验。示例包括基础表格、可选择表格和自定义列渲染等。
657 6
|
6月前
|
JavaScript 前端开发 UED
Vue 项目中如何自定义实用的进度条组件
本文介绍了如何使用Vue.js创建一个灵活多样的自定义进度条组件。该组件可接受进度段数据数组作为输入,动态渲染进度段,支持动画效果和内容展示。当进度超出总长时,超出部分将以红色填充。文章详细描述了组件的设计目标、实现步骤(包括props定义、宽度计算、模板渲染、动画处理及超出部分的显示),并提供了使用示例。通过此组件,开发者可根据项目需求灵活展示进度情况,优化用户体验。资源地址:[https://pan.quark.cn/s/35324205c62b](https://pan.quark.cn/s/35324205c62b)。
277 0
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
586 161
|
JavaScript
在 Vue 3 中,如何使用 v-model 来处理自定义组件的双向数据绑定?
需要注意的是,在实际开发中,根据具体的业务需求和组件设计,可能需要对上述步骤进行适当的调整和优化,以确保双向数据绑定的正确性和稳定性。同时,深入理解 Vue 3 的响应式机制和组件通信原理,将有助于更好地运用 `v-model` 实现自定义组件的双向数据绑定。
848 159