真实高质量低代码商业项目,前端/后端/运维/管理系统
//xia仔のke:
3w ukoou com
低代码介绍
低代码开发是一种软件开发方法,旨在通过最小化手动编写的代码量来加快应用程序的开发速度和部署速度。它提供了一种图形化界面和可视化工具,使开发人员能够使用简单的拖放和配置操作来创建应用程序,而无需深入编写大量的代码。这种方法的核心理念是将开发人员从繁琐的编码任务中解放出来,让他们专注于应用程序的业务逻辑和用户体验设计。
低代码平台通常提供了一系列预构建的组件和模块,开发人员可以通过拖放的方式将它们组合在一起,从而构建出完整的应用程序。这些组件可能包括用户界面元素、数据库集成、业务逻辑组件等。同时,低代码平台通常也提供了自动化工具,用于处理诸如安全性、扩展性和性能优化等方面的问题。
低代码开发有助于加快应用程序的开发周期,降低开发成本,并且使非专业开发人员也能够参与应用程序的开发过程。它适用于各种类型的应用程序开发,从简单的内部工具到复杂的企业级应用程序,都可以借助低代码平台来快速构建和部署。
真实高质量低代码商业项目,前端/后端/运维/管理系统 - 编辑器难点解决方案
-
左侧是预设各种组件模版并进行添加。 - 中间是使用交互的手段更新元素的值。
- 右侧是使用表单的手段更新元素的值。
第一个问题: 整体状态设计
不难看出我们的编辑器其实就是围绕着中间画布的元素来进行一系列操作,那么自然而然着
是一系列的元素组成的,我们应把它抽象成一系列拥有特定数据结构的数组。
export interface EditorStore {
// 供中间编辑器渲染的数组
components: ComponentData[];
// 一系列和其他状态相关的信息,应该有很多
// 当前编辑的是哪个元素,uuid
currentElement: string;
}
interface ComponentData {
// 每个元素 props 所有属性
// 我们上节课已经分析过了,是 css 属性和其他属性的混合体
// 并且我们会把这些属性完全平铺开来,其实在编辑器分析过后,你就能更感受到平铺的一个好处
props: { [key: string]: any };
// id,uuid v4 生成
id: string;
// 业务组件库名称 l-text,l-image 等等
name: string;
}
场景设计
将元素渲染到画布
使用 store 中 compoents 当中的数据,循环渲染
compoents.map(component => <component.name {...props} />)
渲染左侧预设组件模版
原理和上面一样的,只不过数据是预设好的,这个可以写死在本地,也可以从服务器端取得。他们和中间元素不一样的是,这些组件都有一个点击事件,我们可以添加一层 wrapper 来解决这个问题。这样也可以和内部的 lego components 做到隔离,互不影响。
compoents.map(component => <Wrapper><component.name {...props} /></Wrapper>)
添加和删除组件
非常简单的逻辑,向 store 中添加和删除组件即可。
// 添加
components.push({type: '', props: {} })
// 删除
components = components.filter((component) => component.id !== id)
将属性映射到表单
几个典型场景的实现,大家应该发现,其实没那么复杂,就是对全局状态中的 components 字段进行修改而已。
现在我用一张更大的图,来描述应用的整个流程。
现在我们完成了数据到画布渲染这第一步,接下来要到点击画布中的某个组件需要将该元素的属性以不同表单的形式展示到右侧。
一个很容易想到的是直接将这些表单组件写死到页面中去。
const currentComponentProps = {
text: '123',
color: '#fff'
}
<input value={text}/>
<color-picker value={color}/>
...
缺点:
- 代码冗长
- 对不同类型业务组件都要判断
- 可扩展性很差
看到界面展示,应该想到另外一个纬度,界面UI 其实就是数据的抽象,所以我们自然想到的就是使用特定的数据结构将它渲染成界面。
const textComponentProps = {
text: 'hello',
fontFamily: 'HeiTi',
color: '#fff'
}
const propsMap = {
text: {
component: 'input'
},
fontFamily: {
component: 'dropdown',
},
color: {
component: 'color-picker'
}
}
// 这里我们还是循环所有属性,在每个属性中渲染对应处理这个属性的组件
map(textComponentProps, (key, value) => {
<propsMap[key] value={value}>
})
-
当遇到没有类似的 Form 组件的时候,我们可以进行二次开发,只要这个组件有 value 的对应属性
-
这在一定程度上还满足了 可扩展性这个命题,组件的属性可以扩展,对于 color 这个属性,我们自己开发一个取色器或者二次封装一个取色器组件,只要传入 value 属性即可。
组件更新
我们的数据流始终保持自上而下的顺序,也就是说表单更新最终要反射回到总体的 store 当中去。这个时候我们在对应的组件当中发射出一个事件,change,当 change 发生的时候,我们能够知道是哪个元素的哪个属性,以及新的值是什么,我们就用这些信息更新这个值,这样 store 完成更新,元素的 props 发生更新,那么整个数据流动就完成了。
map(textComponentProps, (key, value) => { const handleChange = (propKey, newValue, id) => { const updatedComponent = store.components.find(component.id === id) updatedComponent.props[propKey] = newValue } <propsMap[key] value={value} @change={handleChange}> }
除了表单的更新,还要说一下画布中的交互更新,其实画布中的更新也是采用发射事件的方式对store 的某些值进行更新,比如说拖动改变位置,最终拖动的过程中也是触发对应的change 事件去用相同的逻辑对值进行更新,这里也要注意,我们需要在业务组件外层,添加一个Wrapper,各种事件都是放在这个 Wrapper 上面的,比如支持拖动,改变位置后发送 change 事件。
对于复杂组件也是如此,不管你内部的逻辑有多复杂,添加上传图片,删除,编辑,最终发送出来的事件里面的值,就是对这个 pictures 的值的变换,比如多加了一张照片,那就是数组中的值变成了三项。
画布操作的插件化
比如快捷键,他只写成普通的可重用的函数即可,提供回调即可,在回调中,我们可以对全局 store 进行一系列的改写,而快捷键这个功能和编辑器是没有任何关系的。