RChart 组件
不同于其他基于业务层面封装的 Echart
组件,RChart
仅关注:
- mount(注册)
- unmount(注销)
- watch options(配置项)
- loading(加载动画)、aria(贴花)
- autoResize(自动更新尺寸)
- theme(主题)
换句话讲,RChart
只是帮你做了生命周期管理的事情,让你每次在需要使用 echarts
的时候,只需要去关注配置项的维护。
当然,该组件不仅仅只有以上的能力,这里只是挑一些核心的来举例。
预览
进入正题
由于该组件是
Ray Template
模板中的一个二次封装组件,所以结合了该模板的一些特性在其中。
组件实现
逻辑分析
echarts
注册逻辑
- 注册核心组件、渲染器
- 获取
DOM
- 调用
init
方法注册图表,并且返回一个实例 - 调用
setOption
方法设置配置项
echarts
卸载逻辑
- 调用
clear
,dispose
方法释放资源 - 注销相关监听资源
现在我们知道了
echarts
的完整注册、卸载流程,我们继续分析,如何把它与vue3.x
结合起来。
与 vue3.x
结合
我们应该先分析,需要先做那些问题是 init
时需要关注的:
- 容器的初始尺寸
- 主题
options
配置项
再来看,封装组件需要关注的:
- 代理
optons
配置项 - 代理
echarts instance
实例 - 触发注册成功或失败的回调方法
lodaing
、aria
、theme
代理
代码实现(部分代码)
- 注册
echarts
const renderChart = (theme: string = echartTheme) => {
/** 获取 dom 容器 */
const element = rayChartRef.value as HTMLElement
/** 获取配置项 */
const options = combineChartOptions(props.options)
/** 获取 dom 容器实际宽高 */
const { height, width } = element.getBoundingClientRect()
const { onSuccess, onError } = props
try {
/** 注册主题 */
echartThemes.forEach((curr) => {
echarts.registerTheme(curr.name, curr.theme)
})
/** 注册 chart */
echartInst = echarts.init(element, theme, {
/** 如果款度为 0, 则以 200px 填充 */
width: width === 0 ? 200 : void 0,
/** 如果高度为 0, 则以 200px 填充 */
height: height === 0 ? 200 : void 0,
})
echartInstanceRef.value = echartInst
/** 设置 options 配置项 */
if (props.animation) {
echartInst.setOption({})
setTimeout(() => {
options && echartInst?.setOption(options)
})
} else {
options && echartInst?.setOption(options)
}
/** 渲染成功回调 */
if (onSuccess) {
call(onSuccess, echartInst)
}
} catch (e) {
/** 渲染失败回调 */
if (onError) {
call(onError)
}
console.error('RChart render error: ', e)
}
}
该方法实现了基本的注册逻辑。并且能够在获取容器尺寸失败的时候,使用默认尺寸填充。
- 注销
echarts
const destroyChart = () => {
if (echartInst && echartInst.getDom()) {
echartInst.clear()
echartInst.dispose()
echartInstanceRef.value = void 0
}
}
该方法用于卸载图表释放资源。
- 实现代理并且监听
options
watchEffect(() => {
/** 监听 options 变化 */
if (props.watchOptions) {
watchCallback = watch(
() => props.options,
(noptions) => {
/** 重新组合 options */
const options = combineChartOptions(noptions)
const setOpt = Object.assign(
props.setChartOptions,
defaultChartOptions,
)
/** 如果 options 发生变动更新 echarts */
echartInst?.setOption(options, setOpt)
},
{
deep: true,
},
)
} else {
watchCallback?.()
}
})
允许配置 watchOptions
取消监听
- 自适应尺寸实现
if (props.autoResize) {
resizeThrottleReturn = throttle(resizeChart, props.throttleWait)
/** 监听内容区域尺寸变化更新 chart */
resizeOvserverReturn = useResizeObserver(
props.observer || rayChartWrapperRef,
resizeThrottleReturn,
)
}
完整代码
完整代码这里不贴出来了,建议转移至该处查看:点击查看
。
使用
在调用该组件时,只需要关注于 options
的管理,即可实现动态更新图表状态。
defineComponent({
name: 'REchart',
setup() {
const baseChartRef = ref<RayChartInst>()
const chartLoading = ref(false)
const chartAria = ref(false)
const state = reactive({
loading: false,
})
const baseLineOptions = ref({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985',
},
},
},
legend: {
data: ['Email', 'Union Ads', 'Video Ads', 'Direct', 'Search Engine'],
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
],
yAxis: [
{
type: 'value',
},
],
series: [
{
name: 'Email',
type: 'line',
stack: 'Total',
areaStyle: {},
emphasis: {
focus: 'series',
},
data: [120, 132, 101, 134, 90, 230, 210],
},
{
name: 'Union Ads',
type: 'line',
stack: 'Total',
areaStyle: {},
emphasis: {
focus: 'series',
},
data: [220, 182, 191, 234, 290, 330, 310],
},
{
name: 'Video Ads',
type: 'line',
stack: 'Total',
areaStyle: {},
emphasis: {
focus: 'series',
},
data: [150, 232, 201, 154, 190, 330, 410],
},
{
name: 'Direct',
type: 'line',
stack: 'Total',
areaStyle: {},
emphasis: {
focus: 'series',
},
data: [320, 332, 301, 334, 390, 330, 320],
},
{
name: 'Search Engine',
type: 'line',
stack: 'Total',
label: {
show: true,
position: 'top',
},
areaStyle: {},
emphasis: {
focus: 'series',
},
data: [820, 932, 901, 934, 1290, 1330, 1320],
},
],
})
const handleLoadingShow = (bool: boolean) => {
state.loading = bool
}
const handleAriaShow = (bool: boolean) => {
chartAria.value = bool
}
const mountChart = () => {
baseChartRef.value?.render()
}
const unmountChart = () => {
baseChartRef.value?.dispose()
}
const handleUpdateTitle = () => {
const createData = () => Math.floor((Math.random() + 1) * 100)
baseLineOptions.value.series[0].data = new Array(7)
.fill(0)
.map(() => createData())
baseLineOptions.value.series[1].data = new Array(7)
.fill(0)
.map(() => createData())
}
return {
baseOptions,
baseChartRef,
chartLoading,
handleLoadingShow,
chartAria,
handleAriaShow,
basePieOptions,
baseLineOptions,
...toRefs(state),
mountChart,
unmountChart,
handleUpdateTitle,
}
},
render() {
return (
<div class="echart">
<RChart
title="周销售量"
ref="baseChartRef"
autoChangeTheme
options={this.baseLineOptions}
showAria={this.chartAria}
preset="card"
/>
</div>
)
},
})
最后
后续将会不定时分析 Ray Template
的组件封装逻辑。
感谢大家的阅读,如果觉得该文章对您有所帮助,可以点个赞支持一下~
如果觉得模板不错,可以点一个小星星支持一下~