无极:面向复杂B端项目的低代码平台设计与实践

如果无法正常显示,请先停止浏览器的去广告插件。
分享至:
1. 无极:面向复杂B端项目的 低代码平台设计与实践 吕洋 腾讯-前端开发高级工程师
2.
3. 低代码 「把重复的流程简化、封装起来,并且能用代码完善千变万化的细节」 布局 逻辑 数据 资源编排 样式、内容布局、UI组件等 页面逻辑、工作流、业务模型、 云服务、SCF、钩子和回调 数据库、接口、数据关系 服务编排、Serverless、CI/CD
4. 中后台站点 逻辑 布局 不仅仅是增删改查,还有大量业务逻辑和维护需求 数据 页面 审批 后台任务 设计体系 流程 数据 登录态 组织架构 组件库 权限 服务网关
5. 用户可定制… 组件 云函数 API网关插件 公共逻辑 登录与鉴权 站点布局 页面布局+逻辑+数据 数据源(连接到平台外) 电子流程 数据源(平台托管的DB) 权限 ……
6. 目录 01 页面搭建 02 工程化 & 可维护性 03 应用与展望
7. 灵活的页面搭建 用低代码,搭建具备UI、丰富逻辑和数据的页面
8. 简单的低代码 • 列出组件 + 配置属性,快速成型活动页 • 堆砌输入框,快速成型表单和问卷 • 大量图片和文字,快速成型落地页 • 使用拼凑元素的方式,快速完成简单重复的场景
9. 复杂站点的低代码 • UI复杂:弹窗、卡片、看板、条件展示… • 逻辑与交互复杂:各种联动、校验、流程… • 维护过程复杂:多人开发、多端协同… PowerApps WEDA Mendix Yida Retool
10. 我们的方案 API网关 / Mock • 主要用户群体:开发人员 • 响应式、双向绑定 • 多角色协同开发 接入数据 搭建页面 数据 Model 数据源 数据协议 辅助搭建 浏览器 页面配置 (布局/数据/逻辑) 页面渲染器 开发组件 组件库 、 插件库 访客访问
11. 简单的演示 GET /search?title=关键词 有了 HTTP 接口,快速搭建一个页面 • 电影搜索接口 导入数据源 配置 初始请求 页面存在了数据 快速搭建 接口调用页 修改表格列 卡片列表 卡片内的 逻辑
12. 导入数据源 配置 初始请求 页面存在了数据 快速搭建 接口调用页 修改表格列 卡片列表 卡片内的 逻辑
13. 导入数据源 配置 初始请求 页面存在了数据 快速搭建 接口调用页 修改表格列 卡片列表 卡片内的 逻辑
14. 导入数据源 配置 初始请求 页面存在了数据 快速搭建 接口调用页 修改表格列 卡片列表 卡片内的 逻辑
15. 导入数据源 配置 初始请求 页面存在了数据 快速搭建 接口调用页 修改表格列 卡片列表 卡片内的 逻辑
16. 导入数据源 配置 初始请求 页面存在了数据 快速搭建 接口调用页 修改表格列 卡片列表 卡片内的 逻辑
17. 导入数据源 配置 初始请求 页面存在了数据 快速搭建 接口调用页 修改表格列 基于字段信息 + 组件库,推荐相关组件 一键更换并绑定数据 卡片列表 卡片内的 逻辑
18. 导入数据源 配置 初始请求 页面存在了数据 快速搭建 接口调用页 修改表格列 卡片列表 卡片内的 逻辑
19. 导入数据源 配置 初始请求 页面存在了数据 快速搭建 接口调用页 修改表格列 卡片列表 卡片内的 逻辑
20. 导入数据源 配置 初始请求 页面存在了数据 进入卡片后,可以使用的数据源会变 快速搭建 接口调用页 修改表格列 卡片列表 卡片内的 逻辑
21. 导入数据源 配置 初始请求 页面存在了数据 快速搭建 接口调用页 修改表格列 卡片列表 卡片内的 逻辑 整个页面, 或者卡片里, 都具备相同的灵活布局能力
22. 导入数据源 配置 初始请求 页面存在了数据 就像页面 「卡片」也可以有自己的变量 且每一卡片之间互相独立 快速搭建 接口调用页 修改表格列 卡片列表 在卡片里加一个按钮,点击就拉取电影信息 (另一个接口) 并展示结果到卡片里面 卡片内的 逻辑
23. 导入数据源 配置 初始请求 页面存在了数据 快速搭建 接口调用页 修改表格列 卡片列表 卡片内的 逻辑
24. 页面片 使用 低代码 搭建与维护 有自己的数据、逻辑、生命周期(aka. 作用域) • 常用于:列表、模块复用、弹窗等 • 数据可以由外层注入,或者自己维护 • 组件开发者,可以声明作用域插槽并注入数据
25. 页面片 对标 代码开发的组件 页面片 开发方式 外部输入数据 使用低代码搭建和维护,简单 结合 React/Vue 等框架写代码 ✅ 定义输入后,运行时由上层注入 ✅ 从上下文继承 ✅ 插件注入 ✅ 定义 props 后,运行时由外层输入 ✅ 从 Context 继承 • • • 网址query参数(仅限根页面片) 上层组件、作用域注入的数据 继承自上层作用域 自己内部的能力 互相嵌套 给外部访问 与外部联动 代码开发的组件 ✅ 有自己的状态数据、逻辑、生命周期 拖拽搭建并配置好即可 组件里挖「页面片插槽」:使用 ScopedSlot 占位符+提供数据 作用域上的变量、方法 输入给作用域的东西 作用域内的子组件($refs) 通过组件提供的 ref ✅ 抛出自定义事件,支持 async await ✅ 自定义事件,实现方式很多 • • • Vue 的 实例、defineExpose React 的 useImperativeHandle WebComponent class 实例
26. 如何描述页面片 多层嵌套的大对象 Root Pagelet inputs: data: from: sampleServer params: x-case: sampleListCase x-foo: bar schema: ... mock: ... 当前作用域 { } data: { sampleArray: [ { id: 1, name: "视频", tags: ... }, { id: 2, name: "专辑", tags: ... }, ], submitChanges: () => { ... } } layout: type: pageContainer children: - type: titleText props: text: 一个示例列表⻚ level: 2 - type: cardArray props: ...
27. 如何描述页面片 组件节点可嵌套Pagelets Root Pagelet Layout: 卡片列表组件 当前作用域 { data: { sampleArray: [ { id: 1, name: "视频", tags: ... }, { id: 2, name: "专辑", tags: ... }, ], submitChanges: () => { ... } } ? } - type: cardArray componentId: _uc_cardArray1 props: columns: 1 数据绑定 gap: 24px bindings: v-model: data.sampleArray scopedSlots: item: layout: { type: card, … } inputs: { item, index, … } 子页面片
28. 如何描述页面片 所有的变量引用 都相对于当前 Pagelet 作用域 Root Pagelet Pagelet: item Layout: 一个输入框 Pagelet: item - type: numberInput props: required: false 数据绑定 readonly: false (相对于所在 Pagelet作用域) placeholder: 请输入数字ID bindings: v-model: item.name componentId: _uc_kkwnur1x_ur events: change: steps: […] debounce: false 当前作用域 { } item: { id: 1, name: "视频", tags: ... }, parentScope: { ... } 数据读写、联动、方法调用…… 都是当前作用域的
29. Recap • 逻辑以响应式和双向绑定为主 • 页面片:低代码搭建,有自己的作用域 API网关 / Mock • 页面描述协议:使用嵌套大对象,描述页面片 • 后续的“工程化”,将围绕它们展开 浏览器 数据 Model 数据源 数据协议 辅助搭建 页面配置 (布局/数据/逻辑) 页面渲染/编辑器 如何描述页面片 页面描述协议 组件库 、插件库
30. One More Thing:快速搭建 • 数据源协议中,定义了字段结构 • 组件协议中,定义了“用组件去适配字段”的规则 • 组合在一起,实现 生成页面 + 生成表单 + 替换备选组件 的功能
31. 工程化 低代码开发流程,也能保障可维护性
32. 工程化 & 可维护性 • 不同于传统工程模式,使用的文件目录+文件方式维护工程 • 低代码更多会用交互式、GUI的方式来维护配置以及工程产物 • 对于可维护性的提升,我们应追求“可视化”配套的完整度 多人协作 环境 复用性 部署 Review与重构
33. 工程化 » 挑战 • Review 代码需要:阅读代码 + 查阅文档 + 调试 • Review 低代码页面需要:检查页面配置 + 查阅设计文档 + 调试 逻辑散落在各个角落,难找 直接读配置?嵌套深不见底
34. 工程化 » 检索能力 • 通过关键词(配置值、配置项名称)模糊搜索 • 搜索 + 依赖收集 的能力 • 通过数据依赖关系顺藤摸瓜,梳理逻辑 • 描述 + 定位结果 的能力 (无极页面靠“数据响应式绑定”驱动) 搜索 + 跳转到对应配置项 • 工具 从数据源面板,快速找到依赖了它的组件
35. 背景 • 从数据关系出发,助用户梳理页面逻辑 目的 • 输入页面配置,收集作用域变量的依赖(读写)情况 问题 • 依赖关系会以任意形式,出现在任意层级 • 物料组件的逻辑黑盒中 • 用户配置的绑定,或者在JS表达式里 • 引擎隐含的依赖逻辑 • 依赖关系别名&继承 (依赖 movies.filter 等同于依赖 movies) 方案 • 收集依赖:流水线式处理 + 开放管线定制能力 • 存储和检索:Trie Tree 从数据源面板,快速找到依赖了它的组件
36. 工程化 » 检索能力 目标 • 输入页面配置,收集作用域变量的被读写情况(静态分析) 难点 • 依赖关系会以任意形式,出现在任意层级 • 物料组件的逻辑黑盒中 • 用户配置的绑定,或者在JS表达式里 • 引擎隐含的依赖逻辑 处理方式 • 为每个数据形态,创建处理队列 • 全部队列清空时,收集完毕 不同的“形态” 有不同的处理队列 深层级里,用 JS表达式,引入了依赖 页面配置 à 组件 à 组件事件行为 à 步骤 à 步骤参数表 à 某个参数使用了 JS表达式 à 表达式里,读了数据源
37. 工程化 » 检索能力 form.owner_id ó @/bindings/v-model user.id ó 目标 • toHex ó 输入页面配置,收集作用域变量的被读写情况(静态分析) @/props/desc @/props/desc 处理方式 • 为每个数据形态,创建处理队列 • 全部队列清空时,收集完毕 队列处理流水线:使用 ES6 Generator 编写 • 语法简单,零运行时依赖 • 重构方便 • 可调度(类Fiber的暂停、终止等) 表单项组件@/ 输入框@/children/0 “修改”事件@/events/change 带插值的字符串 请输入ID,例如: {{ toHex(user.id) }} 属性“desc”@/props/desc toHex(user.id)@/props/desc 双向绑定@/bindings/v-model JavaScript AST 解析 全局变量 + 全局 this. . 直接就是一个 DataPath form.owner_id . user.id@/props/desc toHex@/...
38. 背景 • 从数据关系出发,助用户梳理页面逻辑 搜索效率高 方便遍历+聚合 目的 • 输入页面配置,收集作用域变量的依赖(读写)情况 问题 • 依赖关系会以任意形式,出现在任意层级 • 物料组件的逻辑黑盒中 • 用户配置的绑定,或者在JS表达式里 • 引擎隐含的依赖逻辑 • 依赖关系别名&继承 (依赖 movies.filter 等同于依赖 movies) 方案 • 收集依赖:流水线式处理 + 开放管线定制能力 • 存储和检索:Trie Tree 依赖了 data.movies.items 的配置项 • /layout/props/dataSource • /layout/displayCondition • …
39. 工程化 » 检索能力 • 从数据源面板,快速找到依赖了它的组件 • 搜索 + 跳转到对应配置项 • DevTool
40. 工程化 » 检索能力 • 从数据源面板,快速找到依赖了它的组件 • 搜索 + 跳转到对应配置项 • DevTool
41. 工程化 » 检索能力 • 从数据源面板,快速找到依赖了它的组件 • 搜索 + 跳转到对应配置项 • DevTool
42. 工程化 & 可维护性 • 不同于传统工程模式,使用的文件目录+文件方式维护工程 • 低代码更多会用交互式、GUI的方式来维护配置以及工程产物 • 对于可维护性的提升,我们应追求“可视化”配套的完整度 多人协作 环境 复用性 部署 Review与重构
43. 工程化 » 多人协作 改了个什么东西?看不懂 • 两人同时改一个页面,保存的时候互相重置? • 搭建复杂功能,需要单独调试一段时间? • 需要自己的测试环境? 使用 Git 来操作? 配置结构复杂:深层嵌套,晦涩,劝退用户 修改方式丰富:增删改、结构变化… 需要适合的协作工具! 猜我改了什么? (对调了两个组件的位置,然后把一个组件包到了卡片里)
44. 工程化 » 多人协作 • 两人同时改一个页面,保存的时候互相重置? • 搭建复杂功能,需要单独调试一段时间? • 需要自己的测试环境? 在保存的时候,自动合并其他人的修改
45. 工程化 » 多人协作 问题 方案 找到、描述 修改 • 确定如何表达修改操作、找出修改操作 (patches) • 以图的方式存储配置 • 基于两份 patches 进行 Merge、处理冲突 多个人基于一个版本修改后,合并修改 Master Branch A Branch B 难点 配置结构复杂:深层嵌套,晦涩,劝退用户 修改方式丰富:增删改、结构变化… V2 V1 V3 A1 B1 V4
46. Break Up Big JSON 大对象的层级多、语义差、难比较? • 打散大对象,转化为“节点”+“关系” • 基于它,做 Diff/撤销重做/多人协同
47. 张三的修改 更换只读文本 为 Markdown 组件 修改描述信息
48. 李四的修改 新增“双栏”布局 将 2 个 FormItem 放进去 修改 FormItem的标签位置
49. We want to Merge
50.
51.
52. ✅ 找出修改:diff 或者 劫持数据的读写 • 多分支维护 ✅ 三路合并: mergePatches • 多人协同 ✅ 应用修改:applyPatches • 回滚修改 ✅ 回滚操作:获得 commit 的 reversePatches 再 apply
53. Recap: 多人协作 问题 方案 找到、描述 修改 • 确定如何表达修改操作、找出修改操作 (patches) • 以图的方式存储配置 • 基于两份 patches 进行 Merge、处理冲突 多个人基于一个版本修改后,合并修改 在低代码平台里的应用 • 多分支功能 • 多环境(dev、测试环境、正式环境……) 难点 配置结构复杂:深层嵌套,晦涩,劝退用户 修改方式丰富:增删改、结构变化… Master Branch A Branch B V2 V1 V3 A1 B1 V4
54. 工程化 & 可维护性 • 不同于传统工程模式,使用的文件目录+文件方式维护工程 • 低代码更多会用交互式、GUI的方式来维护配置以及工程产物 • 对于可维护性的提升,我们应追求“可视化”配套的完整度 多人协同 环境 复用性 部署 Review&重构
55. 工程化 » 复用 • UI组件 • 逻辑代码 • 页面片:可视化搭建的布局、逻辑 把页面片变成组件,数据即props • 整个站点就是模板 本地开发组件 项目级全局 (Vue/React) 数据、方法 嵌入其他页面片 数据源插件 搭建 区块模板 本地开发 页面插件 仿佛开发了一个组件 项目模板
56. 公共组件库 + 业务私有库 • 丰富的组件生态 • 覆盖管理台通用组件,并配套完善的组件指南 • 开发者工具,支持组件自定义开发
57. 应用与展望 来自真实项目的验证
58. 用户可定制… 组件 云函数 API网关插件 公共逻辑 登录与鉴权 站点布局 页面布局+逻辑+数据 数据源(连接到平台外) 电子流程 数据源(平台托管的DB) 权限 ……
59. 开放平台 / 礼包配置 • 一个开放平台,提供给用户管理、运营APP应用的渠道内容 • 例如:活动、礼包、礼券等,并提供对应的数据看板,展示渠道活动的运营数据
60. 直播管理 / 场次管理 • 一套专业的直播内容管理平台 • 包含直播创建、场次管理、直播监控、主播管理、直播互动等工具 • 同时也支持多租户、以及细致的权限管控
61. 数据中台 • 一个专业的数据中台管理系统 • 用于数据源、数据字段等相关存储与服务管理(元信息、调试、接入、管控等) • 支持多租户接入、权限隔离、多环境切换。
62. 门户站点 / 文档站点 • 无极官方主页、官方文档站,也是用无极搭建的 • wujicode.cn
63. 正确看待低代码 项目的本质复杂度无法消除 开发者可以选择合适的方式,并且多方式搭配使用 使用了自定义组件 完全代码开发的页面 的页面 纯拖拽页面 Ideal Distribution 基于内部使用情况统计
64. 扫码回复「D2」 获取第十七届 D2 演讲 PDF 材料 [无极低代码] 欢迎体验 ? wujicode.cn 后续也将推送 D2 会后技术文章,敬请关注!!
65. 感谢大家观看 wujicode.cn

Home - Wiki
Copyright © 2011-2024 iteam. Current version is 2.137.1. UTC+08:00, 2024-11-17 06:54
浙ICP备14020137号-1 $Map of visitor$