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

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:暂时更新到这了,如果有后续,我回接着更新了

原文地址:https://segmentfault.com/a/1190000012803831

做一个可复用的 echarts-vue 组件(延迟动画加载)的更多相关文章

  1. $nextTick解决Vue组件卸载在加载合并的问题

    情况是这样的,父子组件都是复选框,点击父组件的复选框,子组件的复选框要显示并全选,取消复选框,子组件隐藏.子组件显隐我用的 v-if ,使用created钩子函数来使子组件处于全选状态. 但是出现的问 ...

  2. 原创《分享(Angular 和 Vue)按需加载的项目实践优化方案》

    针对前端优化的点有很多,例如:图片压缩,雪碧图,js/css/html 文件的压缩合并,  cdn缓存, 减少重定向, 按需加载 等等 最近有心想针对 ionic项目 和 vue项目,做一个比较大的优 ...

  3. vue 组件按需引用,vue-router懒加载,vue打包优化,加载动画

    当打包构建应用时,Javascript 包会变得非常大,影响页面加载.如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了. 结合 Vue 的 异步 ...

  4. Vue.js 子组件的异步加载及其生命周期控制

    前端开发社区的繁荣,造就了很多优秀的基于 MVVM 设计模式的框架,而组件化开发思想也越来越深入人心.这其中不得不提到 Vue.js 这个专注于 VM 层的框架. 本文主要对 Vue.js 组件化开发 ...

  5. 【原】从一个bug浅谈YUI3组件的资源加载

    篇前声明:为了不涉及业务细节,篇内信息统一以某游戏,某功能代替 前不久,某游戏准备内测客户端,开发人员测试过程中发现某功能突然不灵了,之前的测试一切ok,没有发现任何异常,第一反应是,游戏内浏览器都是 ...

  6. Vue性能优化之组件按需加载(以及一些常见的性能优化方法)

    关于Vue中的按需加载我就简单介绍一下:大概就是我们所有的东西都会在app.js里面,但是我们并不需要把所有的组件都一次性加载进来,我们可以在需要它的时候再将它加载进来,话不多说,开车! 1.webp ...

  7. Vue 路由懒加载, VueRouter一步完成Vue的路由懒加载 一行代码搞定懒加载

    Vue Router路由配置中的component里面配置即可 1 // 路由懒加载的方式加载组件 2 3 component: () => import('@/views/Detail'), ...

  8. 在webpack中使用Code Splitting--代码分割来实现vue中的懒加载

    当Vue应用程序越来越大,使用Webpack的代码分割来懒加载组件,路由或者Vuex模块, 只有在需要时候才加载代码. 我们可以在Vue应用程序中在三个不同层级应用懒加载和代码分割: 组件,也称为异步 ...

  9. 深入浅出的webpack4构建工具---webpack+vue+router 按需加载页面(十五)

    1. 为什么需要按需加载? 对于vue单页应用来讲,我们常见的做法把页面上所有的代码都打包到一个bundle.js文件内,但是随着项目越来越大,文件越来越多的情况下,那么bundle.js文件也会越来 ...

随机推荐

  1. Pyhton学习——Day34

    # 任何语言都会发生多线程,会出现不同步的问题,同步锁.死锁.递归锁# 异步: 多任务, 多个任务之间执行没有先后顺序,可以同时运行,执行的先后顺序不会有什么影响,存在的多条运行主线# 同步: 多任务 ...

  2. Flex tree展开节点问题!

    问题: 使用 for each(var item:XML in menuTree.dataProvider) {     menuTree.expandChildrenOf(item,true);   ...

  3. pythone 学习笔记(粗略)

    文档目录 概述 安装 基本语法 数据结构 4.1 数字和字符串类型 4.2 元祖 4.3 列表 4.4 字典 流程语句 5.1 分支结构 5.2 逻辑运算符(if) 5.3 循环 5.3.1 for ...

  4. SM32 USART与USB接收不定数据方法,标准库、HAL库都适用

    很多时候,我们使用串口或USB接收数据时,往往不知道PC端会发多长的数据下来, 为了解决这个不定数据接收问题,在此各提供一个解决思路. 串口数据不定接收: 由于STM32单片机带IDLE中断,所以利用 ...

  5. Java实现把两个数组合并为一个的方法总结

    本文实例讲述了Java实现把两个数组合并为一个的方法.分享给大家供大家参考,具体如下: 在Java中,如何把两个String[]合并为一个? 看起来是一个很简单的问题.但是如何才能把代码写得高效简洁, ...

  6. 修改UTC时间

    /sbin/hwclock --systohc date按照时间修正.

  7. C++11时间具体解释

    转载请注明出处:http://blog.csdn.net/luotuo44/article/details/46854229 C++ 11添加了三个与时间相关的类型:时间段.时钟.时间点. 以史为鉴 ...

  8. iOS七种手势

    // 初始化一个UIimageView UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, ...

  9. 什么是域名的TTL值? ——一条域名解析记录在DNS缓存服务器中的存留时间

    什么是域名的TTL值? 转自:http://hizip.net/index.php/archives/20/TTL(Time-To-Live),就是一条域名解析记录在DNS服务器中的存留时间.当各地的 ...

  10. TensorFlow训练MNIST报错ResourceExhaustedError

    title: TensorFlow训练MNIST报错ResourceExhaustedError date: 2018-04-01 12:35:44 categories: deep learning ...