ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

lowcode-engine Workspace 应用级 API 完全指南:基于多窗口模型开发低代码设计器

lowcode-engine Workspace 应用级 API 完全指南:基于多窗口模型开发低代码设计器 lowcode-engine Workspace 应用级 API 完全指南基于多窗口模型开发低代码设计器【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine本指南以 docs/docs/api/workspace.md 为核心结合packages/workspace、packages/types、packages/shell与packages/engine的源码实现系统讲解 lowcode-engine 的 Workspace 模块——一套面向应用级低代码设计器的 API 体系。读完本文你将掌握 Workspace 的变量、方法、事件与配套类型理解资源Resource→ 资源类型ResourceType→ 窗口Window→ 视图EditorView的四层模型并能够在真实项目中基于enableWorkspaceMode搭建多窗口、多资源的低代码设计器。模块定位从页面级设计器到应用级设计器Workspace 模块在 docs/docs/api/workspace.md 中的定位是通过该模块可以开发应用级低代码设计器。它对应since v1.1.0引入的公测能力文档头部明确标注为experimental——根据 docs/docs/api/index.md 的约定experimental表示该模块处于公测阶段API 可能发生变化接入时应注意锁定版本并关注更新。与单文档页面级设计器不同应用级设计器的核心诉求是一个设计器内同时管理多个资源如页面、表单、区块每个资源拥有独立的编辑窗口窗口内部又可以按需切换多种视图如设计视图、源码视图、WebView。Workspace 模块正是为此提供的完整运行时与 API 支撑。在源码层面该能力由独立的 packages/workspace 包实现对外通过packages/shell的Workspace壳层暴露为标准的IPublicApiWorkspace类型定义位于 packages/types/src/shell/api/workspace.ts。入口在 packages/workspace/src/index.ts核心类Workspace实现在 packages/workspace/src/workspace.ts。四层核心模型Resource / ResourceType / Window / EditorView在展开 API 之前先厘清 Workspace 依赖的四层对象模型后续所有方法都是围绕它们运作的模型实现位置职责Workspacepackages/workspace/src/workspace.ts应用级设计器运行时管理资源注册、窗口队列与全局事件ResourceTypepackages/workspace/src/resource-type.ts一类资源的类型定义如 Page、Block决定资源是editor还是webview型Resourcepackages/workspace/src/resource.ts一个具体资源实例某张页面持有资源数据、视图集合与导入/保存钩子EditorWindowpackages/workspace/src/window.ts一个可独立渲染的编辑窗口窗口内再挂载若干EditorView视图上下文EditorWindow内部维护一个WINDOW_STATE状态机packages/workspace/src/window.tssleep睡眠延迟加载、active激活、inactive未激活、destroyed销毁。打开窗口时传入sleep参数即可实现懒初始化这是多窗口场景下控制性能的关键机制。视图层由 packages/workspace/src/context/base-context.ts基础上下文与 packages/workspace/src/context/view-context.ts视图上下文承载每个视图会构建独立的Editor、Designer、Skeleton、Project、PluginManager等实例当视图类型为webview时则通过 packages/workspace/src/inner-plugins/webview.tsx 注册一个内联iframe插件来渲染外部地址。变量VariablesWorkspace 的只读属性均采用 getter 模式导出符合 docs/docs/api/index.md 中属性的导出统一用.xxxgetter的约定。isActive是否启用 workspace 模式get isActive(): boolean;表示当前引擎是否运行在 workspace 模式。在 packages/engine/src/engine-core.ts 中当初始化参数enableWorkspaceMode为真时引擎会调用innerWorkspace.setActive(true)并触发initWindow()随后isActive返回true。window当前设计器窗口模型get window(): IPublicModelWindow;当前激活的编辑窗口。其 Shell 实现见 packages/shell/src/model/window.ts内部持有IEditorWindow若当前没有窗口IPublicApiWorkspace类型声明中其为IPublicModelWindow | null见 packages/types/src/shell/api/workspace.ts。窗口模型IPublicModelWindow的完整属性与方法id、title、icon、resource、importSchema、save、changeViewType等记录在 docs/docs/api/model/window.md。plugins应用级插件注册get plugins(): IPublicApiPlugins;Workspace 层面的插件管理器用于注册/初始化应用级插件与单窗口内的视图级插件相区分。源码中由this.context.innerPlugins提供见 packages/workspace/src/workspace.tsAPI 详见 docs/docs/api/plugins.md。插件注册级别通过IPublicEnumPluginRegisterLevelWorkspace/Resource/EditorView等区分作用域相关逻辑在 packages/workspace/src/context/base-context.ts。skeleton应用级面板管理get skeleton(): IPublicApiSkeleton;Workspace 工作台的面板骨架顶栏、左栏、主区域、底栏等区域的注册与管理API 详见 docs/docs/api/skeleton.md。工作台 UI 布局由 packages/workspace/src/layouts/workbench.tsx 渲染其中TopArea、LeftArea、SubTopArea、MainArea、BottomArea等区域均来自 skeleton。windows当前设计器的编辑窗口列表get window(): IPublicModelWindow[];注意类型声明处源码写法为get windows(): IPublicModelWindow[]文档中笔误为window。这是全部已打开的编辑窗口数组Shell 层在 packages/shell/src/api/workspace.ts 中逐个包装为ShellWindow。窗口列表是 MobX 可观察数据obx.ref windows见 packages/workspace/src/workspace.ts因此新增/删除窗口会驱动 workbench.tsx 自动重渲染。resourceList当前设计器的资源列表数据get resourceList(): IPublicModelResource;资源树列表如左侧资源面板展示的页面列表。Shell 层实现在 packages/shell/src/api/workspace.ts内部调用getResourceList()并将每个IResource包装为ShellResource。资源模型属性title、id、name、type、category、icon、options、config等详见 docs/docs/api/model/resource.md。方法MethodsregisterResourceType注册资源类型/** 注册资源 */ registerResourceType(resourceTypeModel: IPublicTypeResourceType): void;注册一种资源类型。实现位于 packages/workspace/src/workspace.ts将resourceTypeModel包装为ResourceType存入resourceTypeMap并且——一个关键行为——当 workspace 已激活_isActive、尚未创建任何窗口、且存在默认资源类型时会自动调用initWindow()打开第一个窗口。因此注册资源类型通常应在引擎初始化、workspace 激活之前完成。IPublicTypeResourceType定义在 packages/types/src/shell/type/resource-type.ts它本身是一个可调用对象签名如下interface IPublicTypeResourceType { resourceName: string; // 资源类型唯一名称如 Page resourceType: editor | webview | string; // 资源类型编辑器型 / WebView 型 (ctx: IPublicModelPluginContext, options: Object): IPublicResourceTypeConfig; }调用后返回的IPublicResourceTypeConfig定义在 packages/types/src/shell/type/resource-type-config.ts包含字段说明description?资源描述icon?资源图标React 元素或组件defaultViewName默认视图名称editorViews该资源拥有的视图定义列表IPublicTypeEditorView[]init?资源初始化钩子save?保存钩子入参为{ [viewName]: any }的 schema 聚合对象import?导入钩子返回{ [viewName]: any }defaultTitle?默认标题url?当resourceType为webview时返回要渲染的地址IPublicTypeEditorView定义在 packages/types/src/shell/type/editor-view.ts同样是可调用对象(ctx, options) IPublicEditorViewConfigIPublicEditorViewConfigpackages/types/src/shell/type/editor-view-config.ts提供视图级init、save、url钩子。也就是说资源的 save/import 会聚合其下所有视图的 save 结果形成按 viewName 分组的 schema 结构——这一聚合逻辑实现在 packages/workspace/src/window.ts 的EditorWindow.save()与importSchema()中。setResourceList设置设计器资源列表数据setResourceList(resourceList: IPublicResourceList) {}设置资源树列表。实现见 packages/workspace/src/workspace.ts将每项资源数据IPublicResourceData转换为Resource实例通过getResourceType(d.resourceName)找到对应资源类型随后触发resource.list.change事件供onResourceListChange订阅者消费。IPublicResourceData定义在 packages/types/src/shell/type/resource-list.tsIPublicResourceList即其数组类型interface IPublicResourceData { resourceName: string; // 资源名必须对应已注册的 ResourceType config?: { [key: string]: any }; // 资源扩展配置 title?: string; // 资源标题 id?: string; // 资源 Id category?: string; // 分类 viewName?: string; // 资源视图 icon?: ReactElement; // 资源图标 options: { [key: string]: any }; // 资源其他配置作为资源初始化第二参数 children?: IPublicResourceData[]; // 资源子元素支持树形嵌套 }Resource的构造过程packages/workspace/src/resource.ts会按children递归构建子资源并读取editorViews建立viewName → EditorView映射其title、icon等字段支持资源数据优先、资源类型兜底的取值策略如resourceData.title || resourceTypeInstance.defaultTitle见 packages/workspace/src/resource.ts。openEditorWindow打开视图窗口/** * 打开视图窗口 * deprecated */ openEditorWindow(resourceName: string, id: string, extra: Object, viewName?: string, sleep?: boolean): Promisevoid; /** 打开视图窗口 */ openEditorWindow(resource: Resource, sleep?: boolean): Promisevoid;打开或激活一个编辑窗口存在两套签名旧签名按(资源类型名, id, 附加参数, 视图名, 是否睡眠)打开新签名直接传入Resource实例并可选sleep表示延迟初始化。Shell 层在 packages/shell/src/api/workspace.ts 中通过判断首参是否为字符串来分发到openEditorWindow或openEditorWindowByResource。核心实现逻辑packages/workspace/src/workspace.ts值得注意若当前窗口仍在初始化中!window.sleep !window.initReady新请求会被推入windowQueue队列待窗口init()完成后由checkWindowQueue()依次消费packages/workspace/src/workspace.ts若目标资源对应的窗口已存在则直接切换为激活窗口必要时先完成其sleep态初始化若不存在则创建新的EditorWindow追加到windows与editorWindowMap初始化后依次触发emitChangeWindow()与emitChangeActiveWindow()。窗口初始化流程EditorWindow.init()packages/workspace/src/window.ts先初始化全部视图类型 → 执行各视图init→ 等待各视图 simulator renderer ready 后触发onWindowRendererReady→ 解析url→ 设置默认视图 → 标记initReady→ 消费窗口队列 → 激活。openEditorWindowById通过视图 id 打开窗口openEditorWindowById(id: string): void;按窗口唯一 id 激活已存在的窗口。实现见 packages/workspace/src/workspace.ts从editorWindowMap取窗口将旧窗口置为inactive若目标窗口处于sleep态则先完成初始化随后触发emitChangeActiveWindow()并将新窗口置为active。removeEditorWindow移除视图窗口/** * 移除视图窗口 * deprecated */ removeEditorWindow(resourceName: string, id: string): void; /** * 移除视图窗口 */ removeEditorWindow(resource: Resource): void;移除窗口同样提供旧/新两套签名。核心私有方法remove(index)packages/workspace/src/workspace.ts会从windows中剔除该窗口 → 将旧窗口置为destroyed→若被移除的恰是当前激活窗口则自动将焦点切换到相邻窗口优先取同下标其次index 1、index - 1必要时唤醒其sleep初始化 → 依次触发emitChangeActiveWindow()与emitChangeWindow()最后激活新窗口。可见移除激活窗口后的焦点转移是框架自动完成的。removeEditorWindowById通过视图 id 移除窗口removeEditorWindowById(id: string): void;按窗口 id 移除窗口等价于removeEditorWindow的 id 版本packages/workspace/src/workspace.ts。事件Events所有事件订阅均遵循 Disposable 模式返回值为解绑函数调用即可取消订阅。IPublicTypeDisposable定义在 packages/types/src/shell/type/disposable.ts即() void。onChangeWindows窗口新增/删除事件function onChangeWindows(fn: () void): IPublicTypeDisposable;监听窗口列表变化新增或移除。触发点在 packages/workspace/src/workspace.ts窗口打开、移除、initWindow等路径均会调用。典型用途刷新工作台 Tab 栏、保存窗口快照等。onChangeActiveWindowactive 窗口变更事件function onChangeActiveWindow(fn: () void): IPublicTypeDisposable;监听当前激活窗口切换。实现在 packages/workspace/src/workspace.ts同时会级联触发onChangeActiveEditorViewactive 视图变更since v1.1.7见 packages/types/src/shell/api/workspace.ts。onResourceListChange资源列表数据变更事件onResourceListChange(fn: (resourceList: IPublicResourceList): void): (): IPublicTypeDisposable;监听setResourceList导致的资源列表变化回调携带最新的资源列表数据。实现基于内部事件总线emittercreateModuleEventBus(workspace)见 packages/workspace/src/workspace.ts。补充IPublicApiWorkspace类型声明中还包含两个文档未展开的事件——onWindowRendererReadywindow 下所有视图 renderer 就绪since v1.1.7与onChangeActiveEditorViewactive 视图变更since v1.1.7均定义于 packages/types/src/shell/api/workspace.ts需要时可一并使用。启用 Workspace 模式引擎初始化Workspace 模式由引擎初始化参数控制。在 packages/engine/src/engine-core.ts 中if (options options.enableWorkspaceMode) { // 渲染工作台WorkSpaceWorkbench render(createElement(WorkSpaceWorkbench, { workspace: innerWorkspace, ... }), engineContainer); // 是否自动打开第一个窗口默认为 true innerWorkspace.enableAutoOpenFirstWindow engineConfig.get(enableAutoOpenFirstWindow, true); innerWorkspace.setActive(true); innerWorkspace.initWindow(); innerHotkey.activate(false); await innerWorkspace.plugins.init(pluginPreference); return; }关键点enableWorkspaceMode: true是进入 workspace 模式的开关enableAutoOpenFirstWindowengineConfig默认true决定注册首个资源类型后是否自动打开第一个窗口若设为false则initWindow()直接返回packages/workspace/src/workspace.ts由业务侧通过openEditorWindow手动打开首个被注册的资源类型会成为defaultResourceTypepackages/workspace/src/workspace.tsinitWindow()会基于它创建第一个EditorWindow。工作台的渲染结构由 packages/workspace/src/layouts/workbench.tsx 定义TopAreaLeftArea/LeftFloatPane/LeftFixedPane 窗口容器遍历workspace.windows渲染WindowViewMainAreaBottomArea当windows为空且配置了workspaceEmptyComponentengineConfig时展示空状态组件。单个窗口的渲染在 packages/workspace/src/view/window-view.tsx未初始化完成时显示loadingComponent可配置resource.type webview且已解析url时渲染内联iframepackages/workspace/src/inner-plugins/webview.tsx否则渲染ResourceView承载各EditorView。典型开发流程从注册到多窗口管理综合上述 API 与源码行为搭建一个应用级设计器的典型流程如下初始化引擎并开启 workspace 模式import { init, plugins } from alilc/lowcode-engine; await init(document.getElementById(lce-container), { enableWorkspaceMode: true, // 进入应用级设计器模式 enableAutoOpenFirstWindow: true, // 注册首个资源类型后自动打开第一个窗口 // 其他引擎配置... });注册资源类型在init前后均可但需保证 workspace 激活前完成注册以触发自动开窗import { workspace } from alilc/lowcode-engine; workspace.registerResourceType({ resourceName: Page, resourceType: editor, (ctx, options) ({ defaultTitle: 未命名页面, defaultViewName: design, editorViews: [ { viewName: design, viewType: editor, (viewCtx) ({ /* 视图初始化 / 保存钩子 */ }), }, { viewName: preview, viewType: webview, (viewCtx) ({ url: async () /preview.html }), }, ], async save(schema) { /* 按 viewName 聚合保存 schema */ }, async import(schema) { /* 返回 { viewName: schema } 结构 */ }, }), });设置资源列表并监听变更workspace.setResourceList([ { resourceName: Page, id: p1, title: 首页, options: {} }, { resourceName: Page, id: p2, title: 关于页, options: {} }, ]); const off workspace.onResourceListChange((list) { console.log(资源列表已更新, list); });打开 / 切换 / 移除窗口并监听窗口事件// 打开资源窗口sleep 开启延迟初始化 await workspace.openEditorWindow({ resourceName: Page, id: p1, title: 首页, options: {} }, true); workspace.openEditorWindowById(window-xxxx); // 按 id 激活 workspace.removeEditorWindowById(window-xxxx); // 按 id 移除 const offWindows workspace.onChangeWindows(() { /* 刷新窗口列表 UI */ }); const offActive workspace.onChangeActiveWindow(() { /* 高亮激活窗口 */ });通过 window 模型操作当前窗口workspace.window.importSchema(schema)触发资源import钩子并分发到各视图workspace.window.save()聚合各视图save结果后调用资源save钩子详见 docs/docs/api/model/window.md。小结Workspace 是 lowcode-engine 面向应用级低代码设计器场景提供的一套完整方案registerResourceType负责声明资源能力setResourceList注入资源数据openEditorWindow/removeEditorWindow系列方法管理多窗口生命周期三个事件外加since v1.1.7的两个补充事件驱动 UI 与业务联动。其底层通过ResourceType → Resource → EditorWindow → EditorView的分层模型把资源类型定义、资源实例、窗口生命周期、视图上下文彻底解耦配合sleep懒初始化与windowQueue队列在保证多窗口可扩展性的同时控制了初始化成本。该模块当前标注为experimental接入生产环境时建议锁定引擎版本并关注 docs/docs/api/workspace.md 与 packages/types/src/shell/api/workspace.ts 中 API 的后续演进。【免费下载链接】lowcode-engineAn enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系项目地址: https://gitcode.com/GitHub_Trending/lo/lowcode-engine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进