低代码:如何实现不同组件间的级联通信(基础版)
前言
低代码平台,难点在于不同组件间的级联通信,比如下图,左边组件如何绑定用户自定义事件?右边组件如何接收数据,并实现视图刷新?
我的思路如下:
- 找到合适的
Event Emitter
,发射数据 - 将事件代码挂靠到指定元素上,比如点击
全区
、街道
等,发射不同的数据 - 图表组件接收数据,实现视图刷新
事件发射器
该事件发射器使用ts
,其官网如下所示:
我将源码拷贝到项目的utils
目录下,并将事件对象挂靠到window
下,供代码编辑器内使用
绑定自定义事件到元素上
配置json数据内的事件对象
左侧组件,数据来源于初始化json
配置,或用户上传的json
数据,故可在该json
数据内配置各个元素的click
事件,如下图所示:
注意里面的code
,我们会将用户编写的代码赋值到该code
内
将json数据内的事件对象存储到数组内
循环遍历json
配置,将事件对象数据存入到definedEvents
数组内
// 寻找到上传数据内的自定义事件
export const useDefinedEvents = (dataset: any) => {
const definedEvents: any = []
const isEvent = (data: any) => {
if (data.event?.name) {
definedEvents.push(data.event)
}
}
const loopChildren = (data: any) => {
if (data.children?.length) {
data.children.forEach((a: any) => {
isEvent(a)
loopChildren(a)
})
}
}
if (Array.isArray(dataset)) {
dataset.forEach(a => {
loopChildren(a)
})
}
return definedEvents
}
注意:每个event对象,name是其唯一的索引,不可重复
编辑面板显示自定义事件
将组件内的自定义事件,挂靠到对应的targetData
内,代码如下所示:
targetData.value.events.definedEvents = useDefinedEvents(parsedData)
targetData
为当前正在编辑的组件,其源码如下所示:
// 获取当前对象数据
export const useTargetData = () => {
const chartEditStore = useChartEditStore()
const targetData: Ref<CreateComponentType | CreateComponentGroupType> = computed(() => {
const list = chartEditStore.getComponentList
const targetIndex = chartEditStore.fetchTargetIndex()
return list[targetIndex]
})
return { targetData, chartEditStore }
}
我们将当前正在编辑的所有组件存储到chartEditStore
内,通过指定的当前索引targetIndex
,便能找到正在编辑的组件
新增ChartEventDefinedHandle
文件夹,在文件夹下新增index.vue
内,遍历targetData
内的definedEvents
,展示自定义事件函数
注意:对
definedEvents
变量,需使用computed
进行赋值,这样才能监测到targetData
内的definedEvents
变化(我之前使用reactive
并无效果)
界面效果如下图所示:
代码编辑器使用monaco-editor
,它是微软开源的基于 VS Code 的代码编辑器,运行在浏览器环境中,GitHub项目链接:monaco-editor-demos
事件绑定
通过v-on
和new Function
实现动态绑定用户编辑的事件
<template>
...
<div
:class="['right-item', conActive == index ? 'active' : '']"
v-for="(item, index) in dirList.children"
:key="index"
v-on="togAction(item, index)"
>
{{ item.name }}
</div>
...
</template>
<script lang="ts" setup>
const togAction = (item: any, index: number) => {
const { event } = item
const definedEvent: { [key: string]: any } = {}
if (!event) {
definedEvent['click'] = new Function('')()
return { ...definedEvent }
}
const codeStr = event.code || item.code
const generateDefinedFunc = () => {
try {
return new Function(`
return (
async function(){
${codeStr}
}
)`)()
} catch (error) {
console.error(error)
}
}
definedEvent[event.type] = generateDefinedFunc()
return { ...definedEvent }
}
</script>
event.code
即为用户编辑的代码,通过new Function
执行代码字符串。
视图刷新
用户在事件代码内,使用事件发射器,发射图表更新数据
globalEvent.fire('update:LineLinearSingle', {
"dimensions": ["区", "人数"],
"source": [{
"街道": "宝安区",
"人数": 503440
}]
})
图表组件接收数据,更新视图
const vEchartsSetOption = (data: any) => {
const options = cloneDeep(props.chartConfig.option)
options.dataset = data
options.source = data.source
option.value = options
vChartRef.value?.setOption(options)
}
globalEvent.on('update:LineLinearSingle', (evt: any) => {
const { data } = evt
if (data) vEchartsSetOption(data)
})
注意点:用户编辑完代码之后,左侧组件,如何更新元素绑定的
code
代码呢?我使用watchEffect
监听targetData.value.events.definedEvents
变化,再次更改数据源dirList
内的code
若大家有更好的办法,欢迎在评论区留言
watchEffect
代码如下所示:
watchEffect(() => {
const definedEvents = targetData.value?.events?.definedEvents
definedEvents.forEach((a: any) => {
const targetIndex = dirList.value?.children.findIndex(
(b: any) => b.key === a.params.key
)
if (targetIndex > -1) {
dirList.value.children[targetIndex].event.code = a.code
}
})
})
缺陷
当页面上有多个同名组件时,事件发射器便有很大局限了,除非fire
内的第一个参数能被用户动态配置,否则无法得知更新哪个组件。
转载自:https://juejin.cn/post/7239604559398584381