低代码

# 低代码

低代码是⼀种软件开发⽅法,团队可借助此⽅法在编写最少量代码的情况下开发数字解决⽅案并创建企业应⽤程序。 低代码开发平台为⽤⼾提供了⼀套⼯具、⾃定义代码组件和样板脚本。 然后,⽤⼾可以⾼效地开发新流程和应⽤程序‒ 所有这些任务都不需要编写⼤量的代码,也不需要执⾏脚本测试。 这些平台提供具有简单拖放功能的可视化开发环境。

# 笔记

国内低代码平台 (opens new window)

  • 优点:提高开发效率,降低技术门槛,加快迭代发布,提升可维护性可可扩展性,协作性

  • 缺点:有限的灵活性,学习曲线和技术依赖,平台限制和依赖,性能和扩展性,数据安全风险

页面核心构成

  • 组件区:提供可以被反复拖拽的组件
  • 配置区:可以定制化的配置每一个拖到设计区的组件
  • 设计区:可以将组件拖拽到设计区,并移动位置

# 组件

  • 可重用性和模块化
  • 界面一致性和可定制性
  • 交互和事件处理
  • 可配置性和可扩展性
  • 文档和示例

# 可拖拽

Drag Events(拖放事件):

  • dragstart :当拖动操作开始时触发,通常在拖动源元素上使用。
  • drag :在拖动过程中持续触发,通常在拖动源元素上使用。
  • dragenter :当被拖动的元素进入可放置目标时触发,通常在目标元素上使用。
  • dragover :当被拖动的元素在可放置目标上方移动时触发,通常在目标元素上使用。需要阻止默认行为才能接受拖放。
  • dragleave :当被拖动的元素离开可放置目标时触发,通常在目标元素上使用。
  • drop :当被拖动的元素放置在可放置目标上时触发,通常在目标元素上使用。需要阻止默认行为才能接受拖放。
  • dragend :当拖动操作结束时触发,无论是成功放置还是取消拖放,通常在拖动源元素上使用。
import React, { useState } from 'react';

const DraggableElement = ({ id, text }) => {
  const [isDragging, setIsDragging] = useState(false);
  const handleDragStart = (event) => {
    // setData 向本次的拖拽 注入信息    
    event.dataTransfer.setData('text/plain', id);
    setIsDragging(true);
  };
  const handleDragEnd = () => {
    setIsDragging(false);
  };
 
  // 只有配置了draggable属性的元素 才可以被拖拽   
  return (
    <div
      draggable
      onDragStart={handleDragStart}
      onDragEnd={handleDragEnd}
      style={{ opacity: isDragging ? 0.5 : 1 }}
    >
      {text}
    </div>
  );
};
export default DraggableElement;

// 
const handleDrop = (e) => {
   const componentId = e.dataTransfer.getData()
   const emptyComponentData = compDic[componentId];
   
   // ... push到⻚面总JSON data中 }
<div onDrop={handleDrop} ></div>

# 数据与预览

我们需要一套统一的数据,用于承载页面信息 JSON parse

Vue: renderElement('div', {}, [children]) =>

renderElement('div', {class: 'xxx', width: 'xxx'}, [])

{
  "title": "低代码平台",
  "author": "xxx",
  // ... 
  "components": [
    {
      "type": "Button",
      "props": {
        "text": "Click me"
      },
      "children": []
    },
    {
      "type": "Input",
      "props": {
        "placeholder": "Enter your name"
      },
      "children": []
    }
  ]
}

# 设计区实现

import React from 'react';
// 示例按钮组件 
const Button = ({ text }) => {
  return <button>{text}</button>;
};

// 示例输入框组件 
const Input = ({ placeholder }) => {
  return <input type="text" placeholder={placeholder} />;
};

// ⻚面组件 
const Page = ({ title, components }) => {
  return (
    <div>
      <h1>{title}</h1>
      {components.map((component, index) => {
        const Component = componentMapping[component.type];
        return <Component onDrop={() => handleDrop(component.id)} key={index} {...component.props} />;
      })}
    </div>
  );
};
// 组件映射 
const componentMapping = {
  Button,
  Input
};
// 应用程序 
const App = () => {
  const jsonData = {
    // JSON数据   
  };
  return <Page title={jsonData.title} components={jsonData.components} />;
};
export default App;

# 实践:React手写低代码

# 页面布局

  • 左侧物料列表(组件区)
  • 中间编辑器画布(设计区)
  • 右侧属性面板(配置区)

# 组件设计问题:

Q: 组件区如何能知道我的组件库里面有哪些组件?

components 的根目录下 创建 index.js 并export

Q:组件的样式如何通过prop控制?

将styles也作为props传入进组件

Q:组件区的组件和设计区的组件关系是什么?

左侧的组件区内的组件,是一个react组件, 右侧设计区 是通过JSON

# 拖拽逻辑

Q:如何知道拖拽的是哪个组件?

拖拽事件中使用 dataTransfer 对象来携带一些自定义数据

const handleDragStart = (event, componentId) => {
  // 设置拖动数据   
  event.dataTransfer.setData('text/plain', componentId);
};
const handleDragOver = (event) => {
  // 阻止默认行为以允许放置   
  event.preventDefault();
  // 获取拖动数据   
  const componentId = event.dataTransfer.getData('text/plain');  
  // 根据组件标识符识别组件   
  const draggedComponent = findComponentById(componentId);
  // 处理拖放目标元素的逻辑   
  // ...
};

社区拖拽库:

  • https://github.com/atlassian/react-beautiful-dnd
  • https://react-dnd.github.io/react-dnd/about
  • https://github.com/react-grid-layout/react-grid-layout
  • https://craftft.js.org/

# 组件渲染

const DesignArea = ({ components }) => {
  return (
    <div className="design-area">
      {components.map((component, index) => (
        <div key={index} style={component.style}>
          {renderComponent(component)}
        </div>
      ))}
    </div>
  );
};
const renderComponent = (component) => {
  // 根据组件类型,渲染相应的组件   
  switch (component.type) {
    case 'Button':
      return <Button {...component.props} />;
    case 'Input':
      return <Input {...component.props} />;
    // 添加更多的组件类型     
    default:
      return null;
  }
};

# Undo 、 Redo

实现思路:操作历史记录栈用一个数组来保存编辑器的快照数据。保存快照就是不停地执行 push() 操作,将当前的编辑器数据推入 snapshotData 数组,并增加快照索引 snapshotIndex

目前以下几个动作会触发保存快照操作:

  • 新增组件
  • 删除组件
  • 改变图层层级
  • 拖动组件结束时
  1. 全量快照存储: 速度快,但是对存储空间占用极大
  2. ChangeSet存储: 依赖更复杂的changeset生成及解析逻辑

# 部署

  1. Node JSON: 不需要CI/CD直接渲染 => 对服务器压力很小,用户体验不好

  2. webpack build: CI/CD => 服务任务量很大,用户体验会更好

# 备注

# 参考

上次更新: 11/6/2023, 11:12:18 PM
最近更新
01
taro开发实操笔记
09-29
02
前端跨端技术调研报告
07-28
03
Flutter学习笔记
07-15
更多文章>