低代码平台-自定义组件系统

lxf2023-04-05 18:32:01

自定义组件系统是什么

这里的自定义组件系统是基于代码平台的能力扩展出相应的api和工具提供给用户自定义组件的能力。

为什么要写自定义组件系统

低代码平台是把很多通用的东西抽象出来的东西。那么就会诞生一个问题:如果通用能力不满足场景需要怎么办?

举个例子:低代码平台的按钮组件都是通用的,假如此时业务方想要一些符合业务方独有的组件怎么办?

解决办法:

  • 低代码平台去开发

  • 业务方开发

低代码平台打造的是通用平台,通用是一个基本原则。
如果前者的话,平台会夹杂一些业务方的特殊逻辑,这就违背了通用的原则,而后者的话,就没有这些问题,业务方想怎么改都是他们的事情,平台不用关心,平台只要提供对应的api和工具就可以了。

自定义组件开发步骤

  • 工具和平台说明

    工具名称地址能力
    magic-publishwww.npmjs.com/package/mag…发布打包好的组件代码到静态资源服务器上
    magichotreplacementpluginwww.npmjs.com/package/mag…提供组件调试时的热更新能力
    远方低代码平台-----多场景互动页面制作工具,可以制作类似bilibili法外狂徒交互视频這种交互形式的互动页面
  • 初始化组件项目

      可以使用脚手架初始化一个项目,当然你也可以自己搭一个。这里和我们平时搭项目没啥区别
    
  • 组件项目改造

    • 根目录增加component.config.json-组件描述

      {
      "id": "12",
      "name": "按钮",
      "img": "..."
      }
      
    • webpack配置

      // 输出文件改成umd filename固定static/js/bundle.js
          output: {
             filename: "static/js/bundle.js",
             library: "webpackUMD",
             libraryTarget: "umd",
             libraryExport: "default",
           }
           
      // 增加远方代码平台的热更新组件
         plugins: [
         isEnvDevelopment &&
           new MagicHotReplacementPlugin(require("../component.config.json"))
      
    • 入口文件导出组件

      import App from "./App";
      export default App;
      
    • run start script

    • 平台开启调试功能

      低代码平台-自定义组件系统

    • 调试组件 当组件项目运行时,并且开启了平台自定义功能,则会往当前场景添加你自定义的组件 低代码平台-自定义组件系统

    • 组件发布

      打包组件,然后通过magic-publish命令行工具进行发布

    原理

    核心逻辑

    • 调试原理

      低代码平台-自定义组件系统

    • 调试更新原理 低代码平台-自定义组件系统

    • 线上逻辑

      低代码平台-自定义组件系统

核心逻辑代码实现

  • 组件渲染
    原理其实和微组件差不多
export const Custom: React.FC<IProps> = React.memo(({ scriptPath, id }) => {
   const containRef = useRef<HTMLDivElement>(null);

   const renderComponent = () => {
       const current = containRef.current;
       if (current) {
           fetch(scriptPath)
               .then(res => res.text())
               .then(code => {
                   /**
                   1.伪造CommonJS
                   2.执行代码 - 实际上是执行一个匿名函数
                   3.拿到导出的组件
                   4.render一次
                   */
                   const exports = {};
                   const module = { exports };
                   eval(code);
                   const C = module.exports as React.FunctionComponent;
                   ReactDOM.render(<C />, current);
               });
       }
   };
   useEffect(() => {
       const current = containRef.current;
       if (current) {
           renderComponent();
           if (dep.includes(id)) { // 发布订阅 收集组件重新打包后要执行的回调
               dep.on('magicOk', renderComponent);
           }
       }
   }, [containRef]);
   
   return <div className={$style.cutomComponent} ref={containRef}></div>;
  • 热更新原理

    const express = require("express");
    const http = require("http");
    const cors = require("cors");
    class MagicHotReplacementPlugin {
     constructor(componentConfig) {
       const app = express();
       app.use(cors());
    
       const server = http.createServer(app);
    
       //开启一个websocket服务
       const io = require("socket.io")(server, {
         cors: {
           origin: "*", //前端请求地址
           methods: ["GET", "POST"],
           credentials: true,
           allowEIO3: true,
         },
         transport: ["websocket"],
       });
       this.clientSockets = [];
       io.on("connection", (client) => {
         client.emit("ready", componentConfig);
         this.clientSockets.push(client);
         client.on("disconnect", () => {
           /* … */
           const socketIndex = this.clientSockets.indexOf(client);
           this.clientSockets.splice(socketIndex, 1);
         });
       });
       server.listen(1024);
     }
     apply(compiler) {
       compiler.hooks.done.tap("MagicHotReplacementPlugin", () => {
         this.clientSockets.forEach((socket) => socket.emit("magicOk")); // 打包结束 广播
       });
     }
    }
    
    module.exports = MagicHotReplacementPlugin;
    

    这个本来想用webpack提供的热更新能力的,但是遇到一个问题就是当代码重新打包后,HotReplacementPlugin拉取的hot-update.json路径是根据当前的host,看了一下配置发现好像没有提供配置这个路径的能力。 因此就根据热更新的原理重新实现了一个简单的更新逻辑。但是实际上性能还是没有webpack的热更新能力好。webpack的热更新可以去重新加载变更的模块,而我的是重新走一遍渲染逻辑。

  • 渲染sdk的逻辑
    实际上和编辑器的渲染能力一样

写在最后

其实整个系统还是在初步阶段,之后低代码平台还会继续迭代,渲染的方式也会继续探索weex、原生动态化等等。

最后愿诸君心想事成。