欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

榫卯系列 -- 榫卯技术堆栈分解和编辑器设计

最编程 2024-07-08 21:41:16
...

本系统中文名为榫卯,译名Tenon(下文将以Tenon代称)。这个名字诣在用户使用Tenon时可以像榫卯一样精细搭配,以更原始的方式进行组合。Tenon提供丰富物料的同时也在系统设计层面做到了高度*化的实现,用户可以系统提供的丰富钩子中实现自定义逻辑,再搭配上Tenon自研的同构物料系统搭配自研编译器,使得Tenon在不同平台上运行变得可能。并且,Tenon提供渐进式的低代码服务,用户可以选择进行页面级别或系统级别的接入,当用户只需要轻量的页面级服务时,Tenon可以做到针对页面级别的管理,当用户需要系统级服务时,Tenon也有系统级SDK支持。综上所述,Tenon在实现高度*化的同时,提供渐进式服务,使得用户可以选择让Tenon负责软件级的辅助运维。

想直接看代码的可以直接这边请???? Github地址 ⭐(暗示)

体验地址???? Tenon,算了不暗示了,觉得不错GitHub点颗星呀!

Tenon系列概要:Tenon系列 —— 从零开始的“渐进式”低代码平台(可从此找到其他Tenon相关分享文章)

Tenon技术栈分解

包管理器

由于Tenon是一个具有相当复杂度的项目,所以Tenon将不同功能的模块拆分为了不同的包,并且使用monorepo的方式将各个包管理在同一个git仓库下,并且使用pnpm作为node的包管理器,pnpm将一台主机上所有的依赖管理在同一个目录下,在用到的项目中采用软链接的方式将依赖链接到项目中,以此节约硬盘空间,并且pnpm解决了npm3以来的如潘东依赖等问题。

编程语言

Tenon的主要开发语言是Typescript。相比于JavaScript,Typescript有着更好的类型支持,并且有自带的内部编译器支持,可以让我们方便的通过Typescript Compiler对代码做变更。

后端技术栈

Tenon是一个偏重于前端的项目,但这并不是意味着Tenon的后端很薄弱。相反的,由于Tenon的系统特性,Tenon对后端有密集型IO的需求,所以Tenon选择了在密集型IO方面表现优异的NodeJS作为后端的技术栈。不仅于此,Tenon基于Koa的相关套件封装了一套开箱即用的后端框架tenon-node-framework。该框架具有渐进式配置,依赖注入,逻辑分层等能力,为Tenon的后端开发提供了极大的助力,Tenon采用Koa作为底层,通过Koa-router实现路由,通过Koa-static实现静态文件服务,以及一系列Koa系中间件来实现所需功能。数据库层面,tenon-node-framework使用mongoose管理mongodb并封装为Service层的基础能力。如果大家有兴趣后面可以专门写篇文章介绍tenon-node-framework。

前端技术栈

前端方面,Tenon采用了Vue3作为前端的MVVM框架,得益于Vue的响应式系统,Tenon可以方便的管理Model层与View层的关系,并且使用Vue的SFC模式进行开发可以以声明式的方式进行开发,并且Vue在对模板的编译过程中加入了如patch-flag靶向更新与dynamic- children进行拍平树结构比对等多项优化,使得项目上线后的性能能够得到保证。Tenon使用Vite作为前端的构建工具,Vite使用浏览器支持的ES Module作为模块规范,ES Module原生支持懒加载的功能,所以相比于经典的构建工具Webpack,Vite不需要在提供服务前进行依赖图的构建。正因如此,Vite做到了秒开秒用,极大的提高了开发的效率。组件库方面,Tenon选择了字节跳动公司出品的arco-design前端组件库进行开发。

同构物料库

作为Tenon核心的同构物料模块,则是使用了完全自研的物料套件。为了做到多平台的支持,Tenon的物料实现不能依赖任何平台的特性功能,这也就意味着Tenon的物料实现必须是平台无关的。为了做到这一点,Tenon使用Domain Specific Language(DSL)编写物料的视图层代码,并且为这套DSL实现了一个高性能的编译器(同样的,感兴趣的话后面可以专门写篇文章分享)。对于逻辑层,每个物料可以拥有一个逻辑函数,该函数会在物料初始化时执行,并且Tenon会为函数注入一些元数据,物料可使用这些元数据进行不同逻辑的编写。Tenon的核心在于自成体系的组件系统,在不同平台可通过宿主环境的能力构建不同的组件类来管理物料的内部实现。结合上述实现,对应不同宿主平台,实现不同的物料初始化即可做到多平台适用。

Tenon编辑器设计

在将Tenon的技术栈分解后,我们从用户可以最直接能接触到的Tenon编辑器开始为大家一步步揭开Tenon的神秘面纱,下文以tenon-cms这个包名来称呼今天介绍的系统。

作为一个vue3项目,tenon-cms的特点是什么

tenon-cms项目结构

tenon-cms

local-db —— IndexedDB

Tenon使用IndexedDB作为本地存储,IndexedDB相对于其他WebDB有着更似于数据库的操作过程,并且可以直接进行对象存储,这对于需要进行大量对象存储的Tenon来说无异于是最佳的选择,更多关于IndexedDB的信息可以看看阮一峰老师的这篇博客,这里就不再进行赘述。

布局与页面核心分离并分别做懒加载

tenon-cms中将一个页面划分为布局以及页面核心两部分,这样做可以更好的做到页面核心逻辑与布局的解耦,并且布局的可重用性也得到了较大的提升。通过vuex的layout模块来管理当前激活的布局并在路由处使用动态路由进行加载。

image.png

image.png

不同路由可通过路由的meta对象进行layout组件的注入,并在这一步通过vite进行代码的切割即懒加载。由于每个路由项对应的布局以及页面核心都采用懒加载的方式加载,那么在切换路由时的速度就会被拖慢,但是我们不希望在用户点了一个按钮切换路由后看似什么都没发生,然后过了一会啪的一下,整个页面的布局以及页面核心都改变了,这对用户的体验很不友好,Google有个性能指标Layout Shift就是关于这方面的评分。所以说为了Tenon的用户体验,我们不能只是单纯的让页面进行懒加载,我们得让用户知道他点了按钮这会儿功夫,我们已经去做一些事情了。自然而然的,我们就会想到可以在用户切换路由时展示一个loading,所以每个路由项对应的component字段我们都用一个DynamicLoadingComponent去包裹住我们真正的页面核心组件。DynamicLoadingComponent组件将会在两种情况下展现为loading状态,一种是用户进行了路由切换的操作,这会我们得去拉取新页面的布局以及页面核心,所以旧页面的DynamicLoadingComponent将会从展示页面核心状态转变为loading状态。第二种情况就是上面一种情况的后续,当路由切换后并且新页面的布局以及页面核心已经拉取下来了,那么就会展示新的布局以及新的页面核心,但是新的页面核心任然是一个DynamicLoadingComponent,他需要去拉取真正的页面核心并展示出来,在此之前DynamicLoadingComponent都展现为loading状态。

通过这样一次懒加载的接力,在优化了页面加载速度的同时也提升了用户体验,win-win~

image.png

低代码编辑器

上面介绍完了作为一个vue3项目tenon-cms的特点,下面将开始介绍一下tenon-cms的核心业务 —— 低代码编辑器的设计。

编辑器构成

image.png 如封面所示编辑器主要分为三列,左侧为物料库以及组件树,用户可以拖拽物料库中的物料到中部编辑区生成组件,组件树则可查看当前页面组件树的构造。

中间为核心编辑区,物料被拖到编辑区后会被实例化为web平台的组件(tenon-cms特供版),这些组件可以被选中以及二次拖拽调整位置,点击组件可以选中组件,页面右侧就会展现该组件的相关信息及操作。编辑区上方有很多与编辑器相关的操作比如切换编辑与预览模式、真机预览、清除页面组件树、导出当前编辑器状态、加载历史编辑器状态或导入编辑器状态、页面的生命周期管理、页面下的事件管理、页面的状态以及编辑器的缩放等。

右侧为选中组件的属性及操作面板,总共分为4个模块。

  • 基础模块,基础模块主要展示选中组件的一些基本信息以及选中组件在组件树中的操作,比如提取到父级、与相邻的兄弟组件交换位置、复制组件、删除组件,如果组件是Compose-View的话还可以清除子组件。
  • 属性模块,属性模块会根据组件物料提供的schema动态的展示选中组件的属性编辑器,用户可以同过属性编辑器来编辑组件对应的属性(样式也作为一种属性),并且每个属性都支持表达式绑定,用户可以输入任何合法的JavaScript表达式来作为一个属性的值,从而极大的提升了Tenon的灵活性。

image.png

// 输入框物料的schema
{
 "schemas": [
    {
      "type": "object",
      "title": "输入框配置",
      "fieldName": "inputConfig",
      "properties": {
        "model-value": {
          "type": "string",
          "title": "输入框绑定值"
        },
        "type": {
          "type": "select",
          "title": "type",
          "options": {
            "input": "input",
            "password": "password"
          },
          "default": "input"
        },
        "size": {
          "type": "select",
          "title": "输入框大小",
          "options": {
            "mini": "mini",
            "small": "small",
            "medium": "medium",
            "large": "large"
          },
          "default": "medium"
        },
        "placeholder":{
          "type": "string",
          "title": "提示文字"
        },
        "max-length":{
          "type": "number",
          "title": "最大长度"
        },
        "allow-clear": {
          "type": "boolean",
          "title": "是否允许清空输入框",
          "default": false
        },
        "disabled": {
          "type": "boolean",
          "title": "是否禁用",
          "default": false
        },
        "readonly": {
          "type": "boolean",
          "title": "是否为只读",
          "default": false
        },
        "error": {
          "type": "boolean",
          "title": "是否为错误状态",
          "default": false
        },
        "show-word-limit": {
          "type": "boolean",
          "title": "是否显示字数统计",
          "default": false
        }
      }
    },
    {
      "type": "object",
      "title": "输入框插槽配置",
      "fieldName": "slotConfig",
      "properties": {
        "showSuffix": {
          "type": "boolean",
          "title": "是否显示后缀元素插槽",
          "default": false
        },
        "showPrefix": {
          "type": "boolean",
          "title": "是否显示前缀元素插槽",
          "default": false
        }
      }
    }
  ]
}
  • 实例模块,实例模块主要展示Tenon组件实例开放给外部的一些数据以及方法,并且相关方法提供模拟执行的能力。

image.png

  • 事件模块,事件模块通过解析组件物料提供的配置来解析组件允许hook的阶段并开放添加处理事件的能力。用户在页面事件处新建了事件后可在组件开放的hook注册处理事件,组件在对应阶段将会去调用注册的事件,从而实现组件的事件功能。

image.png

image.png

编辑器banner

编辑器的banner提供页面级别的功能,下面逐一介绍一下各功能。

image.png

  • 编辑/预览,提供切换模式的功能。预览模式可以直接在当前页面查看页面在生产环境的大致表现,但是真实表现还是推荐使用真机预览。

image.png

  • 真机预览,真机预览与生产环境共用一套代码 —— tenon-sdk,能最大程度的还原页面在生产环境的表现。

image.png

  • 清除页面组件树,将页面的组件清空。

image.png

  • 导出页面状态,包括组件树以及事件以及生命周期。
  • 读取页面状态,读取历史版本的组件树或导入页面状态。

image.png

  • 事件管理,管理当前页面的事件,相当于一个事件仓库,页面内的所有自定义事件都在这里注册。

image.png

  • 页面状态,可以查看当前页面的状态。

image.png

  • 页面周期,页面也有自己的生命周期可以在对应的hook注册事件实现自定义功能。

image.png

  • 页面缩放,可以缩放页面来进行精细操作,底层使用zoom来进行缩放,因为transform不会影响元素的布局不是我们期待的效果。

本文先介绍到这里,想要了解更多关于Tenon关注Tenon系列即可,我会尽快更新(大概,逃)。

想直接看代码的可以直接这边请???? Github地址 ⭐(暗示)

体验地址???? Tenon,算了不暗示了,觉得不错GitHub点颗星呀!