做一个可复用的 echarts-vue 组件(延迟动画加载)
在 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 组件(延迟动画加载)的更多相关文章
- $nextTick解决Vue组件卸载在加载合并的问题
情况是这样的,父子组件都是复选框,点击父组件的复选框,子组件的复选框要显示并全选,取消复选框,子组件隐藏.子组件显隐我用的 v-if ,使用created钩子函数来使子组件处于全选状态. 但是出现的问 ...
- 原创《分享(Angular 和 Vue)按需加载的项目实践优化方案》
针对前端优化的点有很多,例如:图片压缩,雪碧图,js/css/html 文件的压缩合并, cdn缓存, 减少重定向, 按需加载 等等 最近有心想针对 ionic项目 和 vue项目,做一个比较大的优 ...
- vue 组件按需引用,vue-router懒加载,vue打包优化,加载动画
当打包构建应用时,Javascript 包会变得非常大,影响页面加载.如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了. 结合 Vue 的 异步 ...
- Vue.js 子组件的异步加载及其生命周期控制
前端开发社区的繁荣,造就了很多优秀的基于 MVVM 设计模式的框架,而组件化开发思想也越来越深入人心.这其中不得不提到 Vue.js 这个专注于 VM 层的框架. 本文主要对 Vue.js 组件化开发 ...
- 【原】从一个bug浅谈YUI3组件的资源加载
篇前声明:为了不涉及业务细节,篇内信息统一以某游戏,某功能代替 前不久,某游戏准备内测客户端,开发人员测试过程中发现某功能突然不灵了,之前的测试一切ok,没有发现任何异常,第一反应是,游戏内浏览器都是 ...
- Vue性能优化之组件按需加载(以及一些常见的性能优化方法)
关于Vue中的按需加载我就简单介绍一下:大概就是我们所有的东西都会在app.js里面,但是我们并不需要把所有的组件都一次性加载进来,我们可以在需要它的时候再将它加载进来,话不多说,开车! 1.webp ...
- Vue 路由懒加载, VueRouter一步完成Vue的路由懒加载 一行代码搞定懒加载
Vue Router路由配置中的component里面配置即可 1 // 路由懒加载的方式加载组件 2 3 component: () => import('@/views/Detail'), ...
- 在webpack中使用Code Splitting--代码分割来实现vue中的懒加载
当Vue应用程序越来越大,使用Webpack的代码分割来懒加载组件,路由或者Vuex模块, 只有在需要时候才加载代码. 我们可以在Vue应用程序中在三个不同层级应用懒加载和代码分割: 组件,也称为异步 ...
- 深入浅出的webpack4构建工具---webpack+vue+router 按需加载页面(十五)
1. 为什么需要按需加载? 对于vue单页应用来讲,我们常见的做法把页面上所有的代码都打包到一个bundle.js文件内,但是随着项目越来越大,文件越来越多的情况下,那么bundle.js文件也会越来 ...
随机推荐
- C#RichTextBox复制并跳转指定行
方法一: rTxt.Focus(); //设置文本框中选定的文本起始点 为 指定行数第一个字符的索引 rTxt.SelectionStart = rTxt.GetFirstCharIndexFromL ...
- HDU 2842 Chinese Rings( 递推关系式 + 矩阵快速幂 )
链接:传送门 题意:解 N 连环最少步数 % 200907 思路:对于 N 连环来说,解 N 连环首先得先解 N-2 连环然后接着解第 N 个环,然后再将前面 N-2 个环放到棍子上,然后 N 连环问 ...
- mac上用VMWare虚拟机装Linux-Ubuntu
1.下载vmware fusion for Mac 网上有很多,大家自己找吧 2.安装Vmware 3.序列号可以在网上找,或者淘宝几块钱买一个(记得买对应版本的) 4.之后就下一步,完成就好 安装U ...
- Centos 6/7 忘记root密码处理方法
1. centos 6.x 重置root密码 开机按esc 到下图 按 e 键进入编辑模式,通常选择第二项后按 e 键编辑此项 进入该编辑模式后,在quiet后面输入 simple 或者 1 然后回 ...
- docker 镜像的导入导出
今天使用docker部署asp.net core应用程序时,发现当我们做好基础镜像之后需要把镜像导出到正式环境,因此学习了一下如何从docker中导出镜像: 1.首先通过docker images命令 ...
- RabbitMQ学习总结(2)——安装、配置与监控
一.安装 1.安装Erlang 1)系统编译环境(这里采用linux/unix 环境) ① 安装环境 虚拟机:VMware® Workstation 10.0.1 build Linux系统:Cent ...
- SpringAOP之CGLIB字节码增强
SpringAOP的基础原理就是动态代理 有两种实现方式:1)jdk动态代理 2)cglib动态代理 jdk动态代理和cglib动态代理的区别在于: cglib没有接口(通过继承父类) 只有实现类. ...
- asp.net-DirectoryEntry基本操作入门
第一个实例代码 DirectorySearcher命名空間 (System.DirectoryServices;) DirectorySearcher search = new DirectorySe ...
- Qt实战之酷狗音乐
此项目仅仅实现实现基本功能: 界面的模仿. 歌词功能的实现.歌曲在线试听和下载. 专辑写真的播放. 在线歌词搜索.以及主要的button功能. 界面没有採用设计器. 所有手写规划.这里先放出效果图. ...
- windows上通过vnc连接虚拟机中linux系统
首先要在虚拟机中安装vnc. 虚拟机的设置中要启用VNC连接. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHdzc2c=/font/5a6L5L2T/ ...