背景
最近在用 Vue3 开发一个可视化页面时发生了个奇怪的现象,想实现图表在用户调整窗口大小后重新渲染,防止发生变形,已经在元素上设置了百分比宽高,但它就是不生效。
解决方案
不多废话,先说解决方案。其实很简单,只需要将你的 echarts 实例对象用 shadowRef()
进行创建,而不是使用坑人的 ref()
!
原来的写法
js
import * as echarts from 'echarts';
import { onMounted, ref } from 'vue';
// !!!重点就在这里!!!
const chart = ref(null);
onMounted(() => {
initChart();
// 添加窗口大小变化监听
window.addEventListener('resize', handleResize);
});
const initChart = () => {
const options = { .....};
const chartDom = document.getElementById('chart');
chart.value = echarts.init(chartDom);
chart.value.setOption(options);
}
}
// 处理窗口大小变化
const handleResize = () => {
chart.value && chart.value.resize();
};
修复后写法
js
......保持不变
import { onMounted, shadowRef } from 'vue';
// !!!重点就在这里!!!
const chart = shadowRef(null);
...保持不变
寻找原因
改完之后生效并且不想关注原因的兄弟可以关闭网站继续搬砖了。
元素宽高百分比?
刚开始我从网上找到的解决方案都是围绕在 echarts 所在元素不能设置固定宽高这个问题上,我的写法确实也不对,但是我将宽高改成百分比之后还是不行,并且在浏览器控制台发现了一个奇怪的报错: echart.resize LineView seriesModel.coordinateSystem undefined
coordinateSystem?
基于这个关键词,我又找到了 echarts Github 仓库的 issues 列表里,在这里找到了我们最终的解决方案,感谢这位大善人🙏。

shadowRef ?
这又是个什么 API?我怎么没学过?
既然遇到了,那就学学吧。
shallowRef
在 Vue 3 中用于创建一个 Ref
对象,但是它不会将对象内部的属性进行深层响应式化。这意味着,当对象本身被替换时,shallowRef
会触发更新,但对象内部的属性发生变化时不会触发更新。 使用场景:
- 性能优化: 对于大型对象,尤其是在对象内部的属性变化不应该影响视图更新的情况下,使用
shallowRef
可以提高性能,减少不必要的更新。 - 与第三方库集成: 当你需要将第三方库的对象暴露给组件,并且第三方库的API可能导致对象内部的属性变化,但你不需要让组件对这些变化进行响应时,
shallowRef
可以帮助你避免不必要的更新和性能问题。 - 临时读取: 当你只需要临时读取某个对象的数据,而不需要对这个对象进行响应式跟踪时,
shallowRef
可以避免不必要的跟踪开销。
shadowRef 与 ref 的区别:
ref
会将对象内部的属性进行深层响应式化,即对象内部任何属性的改变都会导致视图更新。shallowRef
只对对象本身进行响应式化,对象内部的属性变化不会触发视图更新。
个人理解可能是 ref 的深层响应化破坏或影响了 echarts 对象本身,从而导致 echarts 实例中的某些方法失效。
总结
刚才我说 ref 坑人其实有失偏颇,大家还是要根据使用场景来选择到底要用哪个 API,显然这个场景下 shadowRef 是好用的。
Vue3,你就学吧~
