@seeyon/udc-runtime-core 是动态应用运行时的核心逻辑。 主要进行了如下工作:
- 页面启动器(初始化页面)
- 数据源管理
- 权限管理
- 页面设置管理
- 页面样式
- 表单管理
- 表单校验
- 事件管理
- 组件资产管理
- 状态管理
- 界面是否只读,是否可编辑
- ………….
核心概念 概念 释义 补充 页面即应用 应用本质提供的是后端的实体定义,以及部分应用级别的参数。对应前端来说是可替换内容。 对于前端来说,业务的逻辑是在页面进行承载,同一个页面逻辑可以通过数据源代理到不同的实体。 本质上,最终运行在客户端的页面实际就是可以复用的页面逻辑加可以替换的应用实体。所以前端会按照页面进行应用划分。 前端缓存策略可能会调整为页面级别,主应用只会缓存基座能力。 具体需要看需不需要支持多版本共存。 UI 模型 (ui-model) UI 模型是产品术语,指页面组件 UI 相关的数据,包含组件值、显示隐藏状态、字体颜色等数据。 组件值和显示隐藏状态是可以通过交互发生变化的,字体颜色等目前不支持设置。 所以对于运行态来说,会将 UI 模型拆分为 3 个对象进行描述: ● 值状态(value-state) - 组件值,更新频繁 ● 视图状态(view-state) - 组件 UI 状态,更新相对不频繁 ● 视图设置(view-setting) - 组件设置,不支持更新
视图状态(view-state) 和 视图设置(view-setting) 伴随产品迭代,会互相转换。比如:产品希望用户通过微流程修改组件字体颜色,这个时候,字体颜色就变成了视图状态(view-state)
运行时上下文 在日常编码的过程中,模块、函数、类都会在其作用域绑定业务的上下文逻辑,以便在实际使用时减少参数的传递。 而在 UDC 运行态,上述过程却是反模式。
- 禁用函数作为参数和返回值得不偿失
- 一旦将上下文数据保存到作用域变量并传递到下游,极易导致数据污染
- 函数交叉调用,最终的调用链非常复杂,可能一个函数在作用域链被多次调用,无形增加了排查问题的成本 所以,涉及业务上下文的切换、传递和使用,UDC 运行态不允许直接将上下文注入到闭包作用域链的变量,只能通过传递 RuntimeContext 的参数,使用统一的 util 读写上下文数据。 为了方便调试,可以在所有需要使用到上下文的函数里面,将 RuntimeContext 打印到控制台。 组件状态三要素 ● 值状态(value-state) - 底层组件的 value,可以为计算值 ● 视图状态(view-state)- 底层组件的 visible 等,可以为表达式 ● 校验状态(validation-state) - 组件值校验相关,底层组件不关联相关逻辑
页面三层架构 页面初始化层 - page/initializer.tsx ● 初始化接口数据 ● 初始化页面和组件 settings 数据(设计态 schema 转换而成的最小化的配置和数据,profile 文件) ● 初始化组件状态 ● 初始化页面资产
页面层 - page/view.tsx ● 初始化页面变量 ● 渲染页面组件
组件层 - ComponentProxy ● 代理组件值状态 ● 代理组件视图状态 ● 处理组件校验状态 ● 读取组件设置 ● 订阅状态更新依赖 ● 代理向前兼容逻辑 为什么需要初始化完了才进入渲染过程,而不能由页面去调用初始化函数: ● 原则上页面渲染依赖的数据,都应该从 props 里面取数据,包括页面里面的任意组件和 utils ● 部分组件和 utils 客观上在页面完成初始化过程中需要使用 props 里面的数据,但是这个时候拿不到这些数据,所以 ○ import 原始数据文件,或者 import 能拿到原始数据的其他 utils ○ 将组件或者 utils 转换成 ejs 通过生成的时候注入数据 导致:
- 混淆构建时和运行时数据,某个流程引用某个不相干模块的原因是因为要获取“构建时”数据
- 过多的 ejs 文件被运行时依赖,逻辑相同但是里面的变量不一样,打包后 hash 不一样,应用之间的重复代码无法缓存命中
- 无法隔离页面初始化阶段和渲染阶段,彼此逻辑交叉,最终不得不依赖页面全量刷新保证一致性 对偶值(pairValue) 伴随组件值状态发生而变化的配对数据,它与值成对出现,为对偶关系,比如: ● displayName 大部分实体下拉菜单中,后端存储的数据是 id 或者 value,但是在界面展示的是 label。每次获取实体数据后,会把实体相关数据存放到 pairValue 里面。 影子值(shadowValue) 影子值状态发生而变化会强制更新值状态,它与值成对出现,为对偶关系,和对偶值类似,一个是因一个是果 大部分实体下拉菜单中,后端存储的数据是 id 或者 value,但是在界面展示的是 label。通过 id 或者 value 获取数据是异步的,所以通过更新 shadowValue 去保证 label 完成渲染。
全局架构
核心功能
页面启动器 功能点思维导图
组件代理 功能点思维导图
渲染逻辑
Internal SDK UDC 相关能力的整体封装
所有方法都是 getXxx(runtimeContext, params) 方式,runtimeContext 是运行时上下文 { // app getUdcAppInfo, // 获取应用信息
// page getUdcPageInParams, // 获取页面入参 getUdcPageInfo, // 获取页面信息 getUdcPageRuntimeVariable, // 获取页面运行时参数 getUdcPageSignature, // 获取页面签名 getUdcPageVariables, // 获取页面变量 setUdcPageRuntimeVariable, // 设置页面运行时参数 setUdcPageVariables, // 设置页面变量
// component getUdcComponentInfo, // 获取组件信息 getUdcComponentSignature, // 获取组件签名 getUdcComponentValue, // 获取组件值 getUdcComponentViewState, // 获取组件视图状态 setUdcComponentValue, // 设置组件值 setUdcComponentViewState, // 设置组件状态 getUdcParentComponentInfo, // 获取父级组件信息 getUdcDataContainerComponentInfo, // 获取组件对应的数据容器信息 getUdcSubComponentViewState, // 获取子组件视图状态 getUdcRecordIdByUiModelString, // 获取组件对应的内部 recordId setUdcSubComponentViewState, // 设置子组件状态 getComponentMetadata, // 获取组件对应的数据源信息 /** @deprecated / useUdcComponentValue, // 订阅组件值变化 /* @deprecated */ useUdcComponentViewState, // 订阅组件视图变化
// datasource getUdcDatasouceMetaInfo, // 获取实体的数据源信息 queryUdcDatasourceDetail, // 查询数据详情 queryUdcDatasourceList, // 查询数据列表 queryUdcDatasourceListSummary, // 查询数据列表汇总信息 saveUdcDatasourceData, // 保存数据详情 saveUdcDatasourceListData, // 保存数据列表 deleteAndSaveUdcDatasourceData, // 删除并保存数据 createUdcDatasourceData, // 本地创建数据 createUdcDatasourceDataFromSelectRows, // 本地创建选中行数据 deleteUdcDatasourceData, // 本地删除数据 getUdcDatasourceData, // 获取本地数据详情 setUdcDatasourceData, // 设置本地数据详情 getComponentValueFromUiModelNode, // 通过UiModel获取组件值 setComponentValueFromUiModelNode, // 通过UiModel设置组件值
// other callUdcPresetFunc, // 调用 UDC 预制方法 getApiUrlByUdcAppInfo, // 获取应用数据 callUdcPresetEventAction, // 调用 UDC 预制方法 execParseUdcExpression, // 调用表达式解析器
getUdcComponent, // 获取组件 getUdcProfile, // 获取描述文件 transformCtpEnumCode, // 枚举值 ID 转换,特殊逻辑 isStandardStructureExpression, // 判断是否是表达式 useUdcAtomicValues_UNSTABLE, // 订阅状态,底层函数,不推荐使用 getUiModelValueByParamString, // 获取UiModel
/** @deprecated / getHasHostAppName, // 是否有宿主应用 /* @deprecated / getAppRequest, // 就版本数据 API /*
- @deprecated
- 该方法只用于老数据兼容,新开发的组件禁止使用
*/
useSubscribePageInParams, // 订阅页面入参
}
编撰人:huhao
快速跳转
