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 依赖的四层对象模型,后续所有方法都是围绕它们运作的:
| 模型 | 实现位置 | 职责 |
|---|---|---|
Workspace | packages/workspace/src/workspace.ts | 应用级设计器运行时,管理资源注册、窗口队列与全局事件 |
ResourceType | packages/workspace/src/resource-type.ts | 一类资源的"类型定义"(如 Page、Block),决定资源是editor还是webview型 |
Resource | packages/workspace/src/resource.ts | 一个具体资源实例(某张页面),持有资源数据、视图集合与导入/保存钩子 |
EditorWindow | packages/workspace/src/window.ts | 一个可独立渲染的编辑窗口,窗口内再挂载若干EditorView视图上下文 |
EditorWindow内部维护一个WINDOW_STATE状态机(packages/workspace/src/window.ts):sleep(睡眠,延迟加载)、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插件来渲染外部地址。
变量(Variables)
Workspace 的只读属性均采用 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.ts,API 详见 docs/docs/api/plugins.md。插件注册级别通过IPublicEnumPluginRegisterLevel(Workspace/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。
方法(Methods)
registerResourceType:注册资源类型
/** 注册资源 */ 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) => IPublicEditorViewConfig;IPublicEditorViewConfig(packages/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.ts,IPublicResourceList即其数组类型:
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): Promise<void>; /** 打开视图窗口 */ openEditorWindow(resource: Resource, sleep?: boolean): Promise<void>;打开(或激活)一个编辑窗口,存在两套签名:旧签名按(资源类型名, 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 栏、保存窗口快照等。
onChangeActiveWindow:active 窗口变更事件
function onChangeActiveWindow(fn: () => void): IPublicTypeDisposable;监听当前激活窗口切换。实现在 packages/workspace/src/workspace.ts,同时会级联触发onChangeActiveEditorView(active 视图变更,@since v1.1.7,见 packages/types/src/shell/api/workspace.ts)。
onResourceListChange:资源列表数据变更事件
onResourceListChange(fn: (resourceList: IPublicResourceList): void): (): IPublicTypeDisposable;监听setResourceList导致的资源列表变化,回调携带最新的资源列表数据。实现基于内部事件总线emitter(createModuleEventBus('workspace')),见 packages/workspace/src/workspace.ts。
补充:
IPublicApiWorkspace类型声明中还包含两个文档未展开的事件——onWindowRendererReady(window 下所有视图 renderer 就绪,@since v1.1.7)与onChangeActiveEditorView(active 视图变更,@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 模式的开关;enableAutoOpenFirstWindow(engineConfig,默认true)决定注册首个资源类型后是否自动打开第一个窗口;若设为false,则initWindow()直接返回(packages/workspace/src/workspace.ts),由业务侧通过openEditorWindow手动打开;- 首个被注册的资源类型会成为
defaultResourceType(packages/workspace/src/workspace.ts),initWindow()会基于它创建第一个EditorWindow。
工作台的渲染结构由 packages/workspace/src/layouts/workbench.tsx 定义:TopArea+LeftArea/LeftFloatPane/LeftFixedPane+ 窗口容器(遍历workspace.windows渲染WindowView)+MainArea+BottomArea;当windows为空且配置了workspaceEmptyComponent(engineConfig)时,展示空状态组件。单个窗口的渲染在 packages/workspace/src/view/window-view.tsx:未初始化完成时显示loadingComponent(可配置);resource.type === 'webview'且已解析url时渲染内联iframe(packages/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),仅供参考