实现组件拖拽功能
目标
实现组件的拖拽。 参考例子:华为低代码编辑器 阿里的编辑器也可以参考,都差不多。华为的源码开放的比较完整,所以就参考华为的了。
个人最终实现效果:
代码
备注:使用了vue3+vite+ts+vue3DnD(拖拽库),虽然实现了功能,但非常简陋。代码量非常少,逻辑清晰很容易上手,很适合学习用和自我改造。框架也是我自己搭的,很简洁很容易上手的一个框架,也很适合自我改造。
实现的思路
个人认为思路是最重要的,思路在,方向对,一切都好说。整个功能零零散散整了快2周,若思路清晰,估计2,3天就可以整完了。我个人思路肯定有很多缺陷,仅供参考,若有大佬发现我的思路有问题,欢迎指正。
左边的组件库与右边的组件配置栏
左边组件库和右边组件配置栏,只要低代码数据规范预设好,难度不大,这两个模块代码加起来估计都不到200行,思路不清晰的小伙伴,直接看我代码即可。
中间的拖拽编辑器
- 思路1: 样式与操作偏向于C端还是B端?
B端则是越简练越好,C端则是希望给用户有良好的交互体验。个人本来是偏向于C端的,想着这编辑器后期是可以给非技术人员用的。后面参考了其他的几个低代码平台,改为偏向于B端。
因为B端实现更容易,代码会简练清晰很多,这样扩展性和兼容性都能更强。如果要给非技术人员用,应该像钉钉宜搭那样,在阿里的低代码平台上封装多一层。低代码平台是B端,但套多一层可转换成C端,没必要把低代码平台做成C端。
- 思路2: 拖拽器集成在一个模块(vue文件)里好,还是拆成几个好?
vue3的dnd拖拽库的示例,拖拽是分成几个模块(vue)文件的。加上平时写业务也习惯于拆分模块,这样逻辑清晰,也便于模块化。
但现实过程中,发现编辑器是要可以无限层嵌套的,实现嵌套时,拆分的多个模块会让代码变得很繁琐。最后编辑器都写在一个模块中,内部进行嵌套。最终实现效果是很好的。整个模块代码没超过200行,逻辑又很清晰。
javascript
// 使用编辑器,SortView为编辑器,直接入参低代码数据即可,组件会自我进行嵌套。
<template>
<div class="h-full w-full bg-[#ffffff]">
<SortView v-bind="componentDate"></SortView>
</div>
</template>
javascript
// 编辑器template的核心代码片段
<template>
<div :ref="setRef" @click.stop="clickComp" :class="{ 'inline-block': !hasChildren, 'w-full': hasChildren, }"
class="relative">
<component :is="componentNode" v-bind="props">
<!-- 在这里自己进行嵌套 -->
<sortView v-if="props.children && props.children.length > 0" v-for="item in props.children" :key="item.id"
v-bind="item">
</sortView>
</component>
</div>
</template>
- 思路3: 编辑器里面的数据流,方向在哪里?
无论vue还是react都是单向数据流,这种方式流向清晰,利于维护和扩展。一开始我也采取这种方式,但编辑器里面的组件是可以无限嵌套的。若数据一层层往下传,又一层层的往上传,逻辑会变得繁琐,担心扩展和兼容性问题。
于是改完采用状态管理器的模式,利用vue3组合式api将低代码数据独立出来管理,懒得使用pinia了。减去注释代码,整个管理器100行代码搞定,调用比pinia更清晰。
但状态管理器有个问题,就是数据流向不清晰,当我组件想获取父组件的内容时,只能拿父组件的id从头开始遍历进行查找,性能扑街也很繁琐。本想将数据平摊开,做个数据索引,便于数据查询。但这种方式繁琐,且也没啥扩展性。因此遇到需要父级数据的情况,还是采用vue自带的inject,单向数据流的方式。
javascript
// 获取父级的数据
const parentIsContainer = inject<any>('compParent');
//将自己的数据传输给下游
provide('compParent', { isContainer })
现在这种状态管理器+vue的Inject的方式,有些繁琐且有耦合度高,以后有时间可以进行优化。比如用类似vue实例树,把数据串起来,这样查找数据方法,也灵活很多。且也解决现在低代码数据和sortitem渲染绑定在一起了,而低代码数据是不能随意改动的,导致sortView不灵活问题。
实现
当有了上述的思路后,实现起来就容易很多了。 使用DND库模仿华为的低代码平台实现拖拽操作,我目前的编辑器属于无敌简陋版,离华为的还差"亿"点点距离。但最基础的功能是实现了的,属于毛坯房,后期再加装修就好。
不会DND的小伙伴,估计得学习一下这个库,难度其实也不高,我后期也发个DND的上手教程,帮大家快速上手。
整个拖拽功能代码量不足1000行,还分成了好几个文件,想了解的小伙伴,直接下载源码跑起来看即可。个人备注写得也是很清晰的,为了以后的扩展,逻辑也是尽量理顺了。
不信的话,看下面的例子:
javascript
/** 当前template状态
状态: 1.是容器但不能移动。
当父级不是容器,自身是容器,就会这种状态。
2.不是容器能移动
普通组件状态。
3.是容器也能移动
父级是容器,本身也是容器。
4.不能移动也不能当容器。(根节点下的内容,不能移动也不能当容器,暂时搁置先)
备注:默认是普通组件(不是容器能移动),当父级不是容器时,子级都是不可移动的。
*/
let type: '1' | '2' | '3' = '2'
setType()
function setType() {
const parentIsContainer = inject<any>('compParent');
console.log('isContainer', props.componentName, isContainer, parentIsContainer?.isContainer)
//若组件父级是容器,自身也是容器,则状态为3
if (isContainer && parentIsContainer?.isContainer) {
type = '3'
}
//若组件父级不是容器,自身也是容器,则状态为1
else if (isContainer) {
type = '1'
}
}
代码目录介绍
一共3个模块,组件库,编辑器,配置类。刚好对应着页面的左中右。