@univerjs/ui 包深度解析:Univer 共享 UI 框架、工作台服务与 Facade UI API 完全指南
【免费下载链接】univerUniver is a full-stack framework for creating and editing spreadsheets / word processor / presentation on both web and server.项目地址: https://gitcode.com/GitHub_Trending/un/univer
@univerjs/ui是 Univer 全栈办公框架(表格 / 文档 / 演示文稿)的共享应用 UI 层,它为所有业务 UI 插件(如@univerjs/sheets-ui、@univerjs/docs-ui)提供工作台(Workbench)渲染、菜单基础设施、对话框、剪贴板、快捷键等通用能力。本文以该包的官方 README 为骨架,结合仓库源码逐层拆解其安装配置、插件注册、内置服务与 Facade UI API,读完即可在真实项目中正确集成并扩展 Univer 的界面体系。
包定位:一个包,四类能力
@univerjs/ui在 Univer 插件体系中扮演"壳"的角色——它自身不实现任何业务编辑能力,而是提供所有业务插件共享的应用框架。根据官方 README 的说明,它包含四大能力:
- 共享应用 UI 框架(Shared Application UI Framework):即工作台(Workbench),负责把头部(header)、工具栏(toolbar)、Ribbon、侧边栏、状态栏等 UI 部件组装成一个完整的编辑界面;
- 工作台服务(Workbench Services):围绕工作台提供布局、部件显隐、主题切换等运行时服务;
- 菜单基础设施(Menu Infrastructure):统一的菜单注册、排序、分组与渲染体系,供各业务插件注入自己的菜单项;
- 对话框与剪贴板服务(Dialogs, Clipboard Services):桌面端对话框、确认框、侧边栏、消息通知,以及浏览器剪贴板读写能力。
其包级概览如表所示:
| Package | UMD global | CSS | Locales | Facade entry |
|---|---|---|---|---|
@univerjs/ui | UniverUi | Yes | Yes | Yes |
该包同时提供独立 CSS、多语言文案(Locale)以及独立的 Facade 入口(@univerjs/ui/facade),在 package.json 的exports字段中分别映射为./lib/es/index.css、./locale/*与./facade。
安装:包管理器与版本约束
安装命令与官方 README 一致,支持 pnpm 与 npm 两种方式:
pnpm add @univerjs/ui # or npm install @univerjs/ui安装时有两点值得注意:
- 版本一致性:官方文档明确要求"保持所有
@univerjs/*包处于同一版本"(Keep all@univerjs/*packages on the same version)。Univer 采用 monorepo 同步发版策略,混用不同版本可能导致依赖服务或类型定义错位。 - peer 依赖:从 package.json 可以看到,
@univerjs/ui声明了react、react-dom(支持^16.9.0至^19.0.0)以及rxjs >= 7.0.0作为 peerDependencies,宿主项目需自行提供这些运行时依赖。其自身依赖则包括@univerjs/core、@univerjs/design、@univerjs/engine-render、@univerjs/icons与依赖注入框架@wendellhu/redi。
快速上手:三步完成 UI 层接入
官方 README 给出的最小使用示例为:
import '@univerjs/ui/lib/index.css'; import EnUS from '@univerjs/ui/locale/en-US'; import { UniverUIPlugin } from '@univerjs/ui'; univer.registerPlugin(UniverUIPlugin); // Merge EnUS into your Univer locale map when this package contributes UI text.拆解为三个步骤:
- 引入样式:
import '@univerjs/ui/lib/index.css'加载包内置的全局样式(源码入口 src/index.ts 中通过import './global.css'引入)。 - 合并语言包:该包贡献了 UI 文案,官方注释建议"当此包贡献 UI 文本时,将
EnUS合并进你的 Univer locale map"。仓库 src/locale 目录下共提供 20 种语言(en-US、zh-CN、zh-TW、zh-HK、ja-JP、ko-KR、de-DE、fr-FR、es-ES、pt-BR、ru-RU、ar-SA、it-IT、vi-VN、pl-PL、id-ID、fa-IR、ca-ES、sk-SK 等),可直接按需导入。 - 注册插件:
univer.registerPlugin(UniverUIPlugin)将 UI 层挂载到 Univer 实例上。
在真实项目中,插件通常携带配置项注册,例如仓库示例 examples/src/sheets/main.ts:
univer.registerPlugin(UniverUIPlugin, { container: 'app', ribbonType: 'grid', customFontFamily: { list: [ { value: 'PingFang SC', label: '苹方(简)', category: 'sans-serif' }, { value: 'Helvetica Neue', label: 'Helvetica Neue', category: 'sans-serif' }, ], // override: true, }, });插件配置详解:IUniverUIConfig 与工作台选项
UniverUIPlugin的构造参数类型为Partial<IUniverUIConfig>,其完整定义位于 src/config/config.ts:
| 配置项 | 类型 | 说明 |
|---|---|---|
container | string \| HTMLElement | 工作台挂载的 DOM 容器,传入元素 id 或元素本身 |
header | boolean | 是否显示头部栏(默认显示) |
toolbar | boolean | 是否显示工具栏 |
ribbonType | 'collapsed' \| 'simple' \| 'classic' \| 'grid' | Ribbon 展示类型,grid为网格化布局 |
customFontFamily | IFontConfig[] \| { override?: boolean; list: IFontConfig[] } | 追加自定义字体列表;传{ override: true }可覆盖内置字体表 |
footer | boolean | 是否显示底部状态栏 |
contextMenu | boolean | 是否启用右键菜单 |
headerMenu | boolean | 是否显示头部菜单 |
disableAutoFocus | true | 禁用 Univer 启动时的自动聚焦 |
override | DependencyOverride | 覆盖包内依赖注入绑定(高级用法) |
menu | MenuConfig | 注入或覆盖菜单配置 |
popupRootId | string | 弹出层(Popup Portal)的根元素 id,默认自动生成 |
avatarFallback | string | 用户头像的兜底图 |
以上配置在插件构造时通过merge合并到默认配置上,其中menu会以merge: true的方式写入IConfigService,其余部分以ui.config为 key 存储(见 src/plugin.ts)。如果设置了disableAutoFocus,插件会把DISABLE_AUTO_FOCUS上下文值写入IContextService。
桌面与移动双插件:UniverUIPlugin 与 UniverMobileUIPlugin
官方 README 明确导出了两个插件类:
UniverUIPlugin—— 桌面端(Desktop)UI 插件;UniverMobileUIPlugin—— 移动端(Mobile)UI 插件。
两者在源码中结构高度一致(见 src/mobile-plugin.ts):插件名分别为UNIVER_UI_PLUGIN与UNIVER_MOBILE_UI_PLUGIN,都通过@DependentOn(UniverRenderEnginePlugin)声明对渲染引擎的依赖(即必须先注册@univerjs/engine-render的插件),关键差异在于注入的控制器不同——桌面版使用DesktopUIController,移动版使用MobileUIController,从而适配不同终端的交互形态。
两个插件均实现了 Univer 插件的三阶段生命周期:
onStarting():通过registerDependencies注册全部服务绑定,并touchDependencies立即激活ComponentsController、IUIController、ErrorController;onReady():激活SharedController与FeatureSearchController;onSteady():激活ShortcutPanelController。
菜单基础设施:从 schema 到服务的完整链路
@univerjs/ui的菜单体系由services/menu目录下的若干服务与类型支撑,并在 src/index.ts 导出UIMenuSchema。相关核心导出包括:
- 类型体系:
IMenuItem、IMenuButtonItem、IMenuSelectorItem、IDisplayMenuItem、IMenuItemFactory、MenuConfig、MenuItemConfig等(src/index.ts); - 服务:
IMenuManagerService/MenuManagerService,负责菜单项的注册、分组与排序(src/index.ts); - 位置常量:
MenuManagerPosition、RibbonPosition、RibbonStartGroup、RibbonInsertGroup、RibbonFormulasGroup、RibbonViewGroup、RibbonOthersGroup、ContextMenuPosition等(src/index.ts),业务插件通过它们把菜单插入到 Ribbon、右键菜单或工具栏的指定分组; - 菜单项类型:
MenuItemType枚举(src/index.ts)。
此外包内还提供mergeMenuConfigs工具函数用于合并菜单配置(src/index.ts),以及getMenuHiddenObservable/getHeaderFooterMenuHiddenObservable用于监听菜单显隐变化(src/index.ts)。
服务层全景:剪贴板、对话框、弹层与协作 UI
插件在onStarting阶段集中注册了大量服务(详见 src/plugin.ts),按职责可划分为几组:
| 服务 | 接口 | 说明 |
|---|---|---|
| 剪贴板 | IClipboardInterfaceService→BrowserClipboardService(lazy) | 浏览器剪贴板读写,支持纯文本、HTML、PNG/SVG/JPEG/WebP/BMP 等 MIME 类型,常量如PLAIN_TEXT_CLIPBOARD_MIME_TYPE、HTML_CLIPBOARD_MIME_TYPE均从 src/index.ts 导出 |
| 对话框 | IDialogService→DesktopDialogService(lazy) | 桌面对话框的打开与关闭 |
| 确认框 | IConfirmService→DesktopConfirmService(lazy) | 确认弹窗 |
| 侧边栏 | ISidebarService→DesktopSidebarService(lazy) | 侧边栏面板 |
| 消息 | IMessageService→DesktopMessageService(lazy) | 轻量消息提示 |
| 通知 | INotificationService→DesktopNotificationService(lazy) | 通知横幅 |
| 画廊 | IGalleryService→DesktopGalleryService(lazy) | 图片画廊预览 |
| 本地存储 | ILocalStorageService→DesktopLocalStorageService(lazy) | localStorage 封装 |
| 工作台 | IWorkbenchService→WorkbenchService | 工作台管理 |
| 布局 | ILayoutService→DesktopLayoutService | 布局状态 |
| 部件 | IUIPartsService→UIPartsService | 内置 UI 部件注册与显隐控制 |
| 上下文菜单 | IContextMenuService/IContextMenuHostService | 右键菜单 |
| 快捷键 | IShortcutService→ShortcutService | 快捷键注册与分发 |
| 平台 | IPlatformService→PlatformService | 平台环境探测 |
| 字体 | IFontService→FontService | 字体表管理 |
| 关闭前 | IBeforeCloseService→DesktopBeforeCloseService | 页面关闭前的拦截 |
| 协作 | IUnitPresenceUIAdapterRegistry | 协作者在线状态 UI 适配器注册表 |
| Ribbon | IRibbonService/IRibbonOverrideService | Ribbon 渲染与覆盖 |
| 运行时作用域 | IUIRuntimeScopeService | UI 运行时作用域 |
其中相当一部分服务以lazy: true方式绑定(如剪贴板、对话框、消息、通知等),意味着只有在首次被注入时才实例化,从而降低启动开销。剪贴板相关命令CopyCommand、CutCommand、PasteCommand与SheetPasteShortKeyCommandName也从 src/index.ts 导出,供业务层直接触发复制粘贴流程。
Facade UI API:用一行代码操作界面
@univerjs/ui提供了独立的 Facade 入口(@univerjs/ui/facade),将 UI 能力暴露给FUniverAPI 对象,使用时需先导入:
import '@univerjs/ui/facade';该入口(src/facade/index.ts)通过FUniver.extend(FUniverUIMixin)混入能力,实现位于 src/facade/f-univer.ts。常用方法示例:
// 获取快捷键控制器,启用 / 禁用快捷键 const fShortcut = univerAPI.getShortcut(); fShortcut.disableShortcut(); fShortcut.enableShortcut(); // 复制 / 粘贴当前选中内容(基于剪贴板命令) await univerAPI.copy(); await univerAPI.paste(); // 动态创建菜单并插入到 Ribbon 的指定分组 univerAPI.createMenu({ id: 'custom-menu', title: 'Custom Menu', action: () => console.log('Custom Menu Clicked'), }).appendTo('ribbon.start.others'); // 打开侧边栏与对话框 univerAPI.openSidebar({ id: 'sidebar-1', header: { label: 'Header' }, children: { label: 'Content' } }); univerAPI.openDialog({ id: 'dialog-1', title: { label: 'Title' }, children: { label: 'Content' } }); // 控制内置 UI 部件显隐 univerAPI.setUIVisible(univerAPI.Enum.BuiltInUIPart.HEADER, false); univerAPI.isUIVisible(univerAPI.Enum.BuiltInUIPart.HEADER); // false // 注册自定义组件与 UI 部件 univerAPI.registerComponent('custom-menu-icon', SmileIcon); univerAPI.registerUIPart(univerAPI.Enum.BuiltInUIPart.CUSTOM_HEADER, () => React.createElement('h1', null, 'Custom Header')); // 追加自定义字体、切换当前渲染单元 univerAPI.addFonts([{ value: 'CustomFont', label: 'Custom Font', category: 'sans-serif' }]); univerAPI.setCurrent('unit2');从实现上看,这些 API 都是对包内服务的薄封装:copy()/paste()执行CopyCommand/PasteCommand(src/facade/f-univer.ts),openSidebar()/openDialog()分别调用ISidebarService与IDialogService(src/facade/f-univer.ts),setUIVisible()/isUIVisible()委托IUIPartsService(src/facade/f-univer.ts),addFonts()则逐个交给IFontService(src/facade/f-univer.ts)。
与业务 UI 插件的协作关系
在 Univer 的插件体系中,@univerjs/ui处于最底层,业务 UI 插件(如@univerjs/sheets-ui、@univerjs/docs-ui)依赖它提供的菜单、对话框、剪贴板等服务来呈现自己的界面。以官方示例 examples/src/sheets/main.ts 的注册顺序为证:
- 先注册
UniverRenderEnginePlugin(渲染引擎); - 再注册
UniverUIPlugin(UI 层,依赖渲染引擎); - 随后注册
UniverDocsPlugin、UniverSheetsPlugin等业务插件; - 最后注册
UniverDocsUIPlugin、UniverSheetsUIPlugin等业务 UI 插件。
同时示例中通过import '@univerjs/ui/facade'激活 UI 的 Facade API(examples/src/sheets/main.ts),并在创建 Univer 实例时配置locale与locales(examples/src/sheets/main.ts),对应 README 中"将 UI 语言包合并进 locale map"的要求。
总结
@univerjs/ui是 Univer 界面体系的基石:它以UniverUIPlugin/UniverMobileUIPlugin两个插件承载桌面与移动端的工作台,以IUIController区分平台实现;通过IMenuManagerService、IUIPartsService、IDialogService、IClipboardInterfaceService等十余个服务为上层业务插件提供菜单、部件、弹层与剪贴板能力;并借助@univerjs/ui/facade将上述能力以univerAPI的链式方法开放给开发者。无论你是要集成 Univer 的完整 UI,还是仅为自己的业务插件注册一个菜单、弹出一个对话框、追加一个自定义字体,这个包都是绕不开的入口。
【免费下载链接】univerUniver is a full-stack framework for creating and editing spreadsheets / word processor / presentation on both web and server.项目地址: https://gitcode.com/GitHub_Trending/un/univer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考