在无情的增删模块页面之后,客户终于意识到我们的产品还缺少什么东西了,那就是一套看起来就很扎眼球统计看板页,反复压榨我们产品设计之后(好像是六稿,我们开发过程中还改了两稿)
最后差不多是这个样子...
两层tab页,每个页面差不多三四个图表,就这平平无奇的页面,竟然藏着60个图表
设计: 看看这里,三层呢,客户就要这样
还能怎么办?爱他就要为他码代码
准备
分析一下需求
- 技术栈:vue
- 图表数量多
- 花样少 就饼图 折线图 柱状图
- 结构重复,适合组件化
使用echarts作为图表库,为了方便控制,我使用指令来控制图表配置
使用vue自定义指令绘制图表
Vue 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令
注册全局指令
<div v-echarts:[isload].clear="option"/>
<!-- isload参数 控制图表是否加载中 对应echarts实例showLoading() 和 hideLoading()
默认为false,不显示加载 -->
<!-- clear修饰符 使用后 图表绘制前执行一次 echarts实例的clear() -->
const directiveObj = {
inserted(el, binding) {
const charts = echarts.init(el, "themeName");
obz.add(el); // 添加到监听 尺寸变化时 运行echarts实例resize() 后续讲解
updateCharts(charts, binding); // 绘制数据到图表
},
update(el, binding) {
const charts = echarts.getInstanceByDom(el);
updateCharts(charts, binding);// 绘制数据到图表
},
unbind(el) {
const charts = echarts.getInstanceByDom(el);
obz.remove(el);// 移除监听
charts.dispose(); // 释放实例
}
}
app.directive("echarts", directiveObj);// 添加全局自定义指令
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有
unbind:只调用一次,指令与元素解绑时调用。
上述代码,我们可以看出来,在元素插入时,我们echarts.init了一个实例,后面就根据指令传入的数据执行更新图表;
数据更新时,我们通过echarts.getInstanceByDom拿到了当前echarts实例,对它进行更新操作
卸载时,我们echarts.getInstanceByDom拿到实例后,执行charts.dispose()销毁实例
更新图表数据
接下来就要讲解 我们的updateCharts方法是如何实现的
const updateCharts = (charts, binding) => {
if (typeof binding.arg === "boolean") {
if (binding.arg) // v-echarts:[true]
charts.showLoading("default", {
text: "",
color: theme.color[0],
textColor: "#000",
maskColor: "rgba(255, 255, 255, 0.4)",
zlevel: 0,
fontSize: 50,
showSpinner: true,
spinnerRadius: 30,
lineWidth: 4
});
else charts.hideLoading(); //v-echarts:[false] 或者压根就没使用isload参数
}
if(binding.modifiers.clear) // 存在 v-echarts.clear
charts.clear()
if (binding.value)
charts.setOption(binding.value);
charts.resize();
}
binding.value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
binding.arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"
binding.modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
我们首先判断当前isload参数,控制加载面板显示隐藏,再判断clear修饰符控制是否加载前清除图表元素,最后再resize适配尺寸
适配大小尺寸
pc页面,免不了放大缩小尺寸,一些情况下咱们就添加document的resize事件,但是图表一多,我们就很难进行控制了,所以我们使用了ResizeObserverAPI,但是出于兼容性考虑,我们使用'resize'事件做下降级
class ObserveSize {
constructor(cb, delay = 300) {
if ("ResizeObserver" in window) {
this.obz = new ResizeObserver(
// 添加防抖
debounce((targets) => cb(targets.map(({ target }) => target)), delay)
);
} else {
this.domSet = new Set();
this.onResize = debounce(() => {
const { domSet } = this;
if (domSet) cb([...domSet]);
}, delay);
}
}
add(target) {
if (this.obz) {
this.obz.observe(target);
} else if (this.domSet) {
if (this.domSet.size === 0 && this.onResize)
window.addEventListener("resize", this.onResize);
this.domSet.add(target);
}
}
remove(target) {
if (this.obz) {
this.obz.unobserve(target);
} else if (this.domSet) {
this.domSet.delete(target);
if (this.domSet.size === 0 && this.onResize)
window.removeEventListener("resize", this.onResize);
}
}
}
const obz = new ObserveSize((targetList) => {
for (const target of targetList) {
const charts = echarts.getInstanceByDom(target);
if (charts) charts.resize();
}
})
前方的ObserveSize类 提供了两个方法remove add,就是添加控制,new ObserveSize传递回调函数,当尺寸变化时,会传递当前尺寸变化的dom元素 ,针对的处理下charts.resize()就可以了;本文不涉及这个ObserveSizeAPI
强行结尾
之前看大佬写的文章通俗易懂,现在自己写的却是这鬼样子,好多想表达的知识都没有表达,比如生命周期、用管理可复用的元素,这就是石立差距吧。算了,时间到了不睡就猝死的时间了,后期再修改了