🌞

前后不解耦的低代码平台实现前端现代化

讲在前边一切基于组件有这么一种低代码平台,可视化设计器基于一套规范的DSL规范,设计完成之后,通过此DSL依靠后端生成前端页面。这样的方式带来的优势是搭建生成的系统,后期前端维护基本和平台结构,不存在

文链接在语雀:https://www.yuque.com/wumingshi/rkh1qq/

讲在前边

一切基于组件

有这么一种低代码平台,可视化设计器基于一套规范的DSL规范,设计完成之后,通过此DSL依靠后端生成前端页面。

这样的方式带来的优势是搭建生成的系统,后期前端维护基本和平台结构,不存在关键语法糖信息,适合系统的快速二次开发。

然鹅带来的未来当然也不少。

  • 版本极其分散,后期集中升级难度大。
  • 项目整体升级难度大,尤其针对页面的优化等几乎不可实施。
  • 源码无限暴露,“杂草”丛生

这里特别耦合的是一个前端组件严重依赖一个JAVA jar包,即每新增一个前端组件都要开发一个jar包,除了开发效率低本身之外,组件之间的联动、事件通知等也是依赖组件内的各种配置模板以及和jar包的映射关系。同时开发一个组件需要开发两套,一套针对设计器;一套针对用户页面。

不知道是否对平台新增组件的能力的复杂有了一个概述。总之,当前的低代码平台增加一个自定义组件,尤其是具备一些事件等和其他组件有联动的组件,耗时巨大,出错概率高,给平台稳定性带来了极大的不确定性。即使针对一个有经验的前端上手成本也极高。

前端的根基-组件

按照刚才介绍的模式,开发一个自定义组件,在前端侧大致是:

除此之外,后端也需要配合引入一个jar包,或者实现一个相对通用的jar包。

如何前进一小步

事实上,当前系统中已经存量了不少的组件,而这些组件本身对于页面的交互、事件等已经完成了覆盖。例如,点击一个按钮从一个组件中取值,赋值到另外一个组件。系统中的交互、事件等已经能够完全满足,包括从数据库中获取数据复制到组件上等等。

所以目前平台上的组件已经能够满足大部分场景,如果针对平台组件实现进行调整是否就已经满足了所有场景

按照此种思路:

  • 能够在现有的基础上完成前后端解耦
  • 平台稳定性能够得到保证
  • 上手难度降低,只需要关注组件本身,设计器级及页面的联动等复用现有能力
  • 组件独立部署,能够按需加载
  • ...

架构实现思路

技术实现:

组件生态:

本地组件开发

个中细节就不再阐述了

组件离线部署

这里还要额外考虑项目需要完全离线部署,即组件还是需要在构建的时候内置到项目中。这里通过实现一个自定义插件插件即可,

大致实现方式:

compiler.hooks.emit.tapAsync(
      "RemoteCustomFilePlugin",
      async (compilation, callback) => {
        try {
          const configFile = fs.readFileSync(this.options.configFile, "utf8");
          const config = JSON.parse(configFile);
          const cacheDir = path.resolve(
            process.cwd(),
            "node_modules",
            ".remoteCustomComponent"
          );

          async function createFileWithDirectories(_filePath, _content) {
            try {
              // 获取目录路径
              const dirPath = path.dirname(_filePath);
              // 递归创建目录
              fs.mkdirSync(dirPath, { recursive: true });

              // 创建并写入文件
              fs.writeFileSync(_filePath, _content, "utf8");
            } catch (error) {
              console.error("创建文件时出错:", error);
            }
          }

          const promises = config.list.map(async (item) => {
            const filePath = `${item.name}@${item.version}/dist/${item.name}.umd.js`;
            let content = "";
            if (fs.existsSync(path.resolve(cacheDir, filePath))) {
              content = fs.readFileSync(
                path.resolve(cacheDir, filePath),
                "utf8"
              );
            } else {
              const response = await axios
                .get(`${this.options.server}/${filePath}`, {
                  headers: this.options.headers,
                  timeout: this.options.timeout,
                })
              content = response.data;
              await createFileWithDirectories(path.resolve(cacheDir, filePath), content);
            }
            if (content) {
              compilation.assets[`remoteCustomComponent/${filePath}`] = {
                source: () => content,
                size: () => Buffer.byteLength(content, "utf8"),
              };
            } else {
              return Promise.reject(
                new Error(
                  `RemoteCustomFilePlugin: Failed to fetch ${item.name}: empty content`
                )
              );
            }
          });

          await Promise.all([...promises]);
          callback();
        } catch (error) {
          // 错误处理
          const errorMsg = `RemoteCustomFilePlugin: Failed to fetch ${error.message}`;
          // 无fallback时报告错误
          compilation.errors.push(new Error(errorMsg));
          callback(new Error(errorMsg));
        }
      }
    );

写在最后

看似简单的一小步,其实对平台的意义挺大。毕竟已经伟大不掉,只能微创了

updatedupdated2025-03-242025-03-24