​在这篇文章中,我们将从 Univer SDK 的接口层 Facade API 说起,讨论我们如何让 Univer 更易用,服务更广泛的用户群体。

Univer 的设计目标

Univer SDK 的架构设计从一开始就确立了以下几个目标:

  1. 通过插件系统灵活扩展非核心功能,支持用户个性化需求,并允许用户和社区开发插件。
  2. 高度可定制和可扩展,以满足复杂应用的需求。
  3. 为大型项目提供长期的可维护性和可测试性。
  4. 提供卓越的开发者体验。

为实现这些目标,我们引入了诸多机制,如插件系统、依赖注入和命令系统等。然而,这些机制的引入也带来了副作用:直接使用 Univer 的底层 API 开发门槛较高,可能会影响开发者体验——这一点与我们的目标“提供卓越的开发者体验”有所冲突。

幸运的是,“计算机科学中的所有问题都可以通过添加一个间接层解决”,我们引入了 Facade API 作为 Univer SDK 的接口层,封装内部复杂度,为用户提供直观、易用的 API 接口。

官网链接:Univer Go
官网内有我们详细的视频、图文教程,下载后还可一键使用甘特图、表格权限、表格像素画、贪吃蛇等多样化模版,同时我们也欢迎大家向我们提出模版需求

简单易用的 Facade API

使用 Facade API 非常简便。如果你使用 Univer Presets,我们会自动为你创建 univerAPI,你可以立即开始开发:

const { univerAPI } = createUniver({
  locale: LocaleType.ZH_CN,
  locales: {
    [LocaleType.ZH_CN]: merge(
      {},
      UniverPresetSheetsCoreZhCN,
    ),
  },
  theme: defaultTheme,
  presets: [
    UniverSheetsCorePreset({
      container: 'app',
    }),
  ],
});
 
const sheet = univerAPI.getActiveWorkbook().getActiveSheet();
const range = sheet.getRange('A1');
range.setValue('Hello, Univer!');

可扩展的接口设计

在 Facade API 的初期实现中,我们将所有功能集中在一个单独的 @univerjs/facade 包中。然而,这带来了以下这些问题:

  1. 即使用户没有引入某个插件,Facade API 中依然会出现该插件的代码提示,影响开发体验;
  2. 即使某个插件未被使用,由于 Facade API 的实现引用到了这些插件,因此底层实现仍会被引入到用户的构建产物中,导致额外的体积。

为了应对这些问题,我们对 Facade API 进行了重构。

首先,我们引入了接口类、扩展 mixin 和扩展机制。接口类对应原始的 Facade API 类型(如 FUniverFWorkbook 等),都继承自 FBaseFBase 提供了 extend 静态方法,允许为这些接口类添加扩展 mixin。扩展 mixin 本质上也是 TypeScript 类,在继承接口类的基础上,添加新的方法以增强接口功能。

接着,我们将原来的接口拆分成多个功能模块。例如,FWorksheet 被拆解为以下几个文件:

这种拆分方式使得每个文件只包含相应插件的 API,确保 Facade API 的功能模块化,降低了不必要的代码依赖。

我们还增强了 TypeScript 的智能提示功能。通过 declare 关键字,我们能告诉 TypeScript 接口类的类型已被扩展。例如,sheets-ui 包的代码如下:

FWorksheet.extend(FWorksheetSkeletonMixin);
declare module '@univerjs/sheets/facade' {
    interface FWorksheet extends IFWorksheetSkeletonMixin { }
}

最后,我们为所有提供 Facade API 的包创建了二级入口点,允许用户通过二级入口点引入相应的 API。例如,使用 sheets-ui 的 Facade API 时,用户需要这样导入:

import '@univerjs/sheets-ui/facade';

通过这些优化,我们成功解决了之前提到的两个问题:

  1. 只有在引入相应包的 Facade API 时,用户才能看到该包相关 API 的智能提示,避免了无关功能的代码提示;
  2. 同时,相关代码才会被打包进最终产物中,避免了不必要的代码冗余。

浏览器和 Node.js 同构

在一次重构中,我们严格规范了每个插件的运行环境,并尽可能复用浏览器和 Node.js 环境中的代码。目前,你可以在 Node.js 上运行 Univer,实现服务端读写和计算,甚至将它作为无头协同客户端加入 Univer 的协同编辑系统。

同样,Facade API 也可以在浏览器和 Node.js 环境中运行。唯一的区别是,一些包(例如以 ui 作为结尾的包)仅能在浏览器中运行,因此 Node.js 环境无法访问这些包所提供的 Facade API。不过,对于大多数场景,我们已经实现了一处编写,双端运行

Uniscript 与 Univer Go

如果 Facade API 已足够简单易用,我们不仅能让 web 开发者使用,甚至可以让非专业开发者体验 Univer 的强大功能。毕竟,Univer 的目标是帮助每个人和组织构建定制化的生产力工具,而不仅仅服务于专业开发者或软件公司。

最初,我们尝试通过 Uniscript 插件来实现这一目标。用户在安装插件后,可以在侧边栏编写 Uniscript,调用 Facade API 来实现自定义的业务逻辑。例如,绘制一个像素风格的 Univer Logo:

图片

然而,我们很快发现,这种方式无法充分释放 Univer 的潜力,尤其是在自定义 UI 和编写服务端代码方面;另外,这样的产品形态也过于简单,无法满足较为深度的使用需求。

为此,我们推出了全新产品 Univer Go。在 Univer Go 中,你不用再担心类似设置开发环境、部署代码等技术问题,就可以创建项目、编写浏览器端和服务端 Uniscript,探索更多的可能性,例如:

自定义甘特图模板,并设置右键菜单,一键插入到电子表格中:

https://www.bilibili.com/video/BV12bwnehE7Z/?

编写服务端脚本,从数据库获取数据并加载到电子表格:

https://www.bilibili.com/video/BV1MrwJeeEDM/?

甚至可以用 Univer Go 编写一个贪吃蛇小游戏!

https://www.bilibili.com/video/BV1u6wJeNERV/?

持续提升 Facade API 的能力和易用性

为了解锁更多功能,我们的工程师最近为 Facade API 添加了大量新特性。幸运的是,Univer Go 基于 Univer SDK 构建,因此无论你使用的是 Univer SDK 还是 Univer Go,都可以在未来的版本中体验这些新增功能!

我们还在不断提升 Facade API 的易用性。例如,Univer SDK 用户可以通过 presets 引入 Facade API,无需手动引入 @univerjs/xxx/facade 目录;而 Univer Go 用户则可以在内置编辑器中获得完整的类型信息和智能补全,我们还计划在 Univer Go 中引入 AI 辅助编码。此外,我们还花了大量时间和精力来优化文档,确保用户能获得良好的开发体验。

今天我们深入探讨了 Facade API 的设计理念与实现过程,还介绍了我们基于 Facade API 持续为用户提供方便、强大的使用体验的所付出的努力。Univer 的目标不仅仅是为专业开发者提供强大的功能,还希望让非技术用户也能轻松构建定制化的生产力工具。未来,我们将继续倾听用户反馈,迭代产品功能,让每个用户都能充分发挥 Univer 的潜力,构建属于自己的数字化工作环境。无论你是专业开发者,还是业务领域专家,Univer 都将是你值得信赖的伙伴。

如果你还不知道 Univer 是什么:Univer SDK 是一个同构的全栈开发框架,支持在浏览器端和服务端创建与编辑电子表格和文档。它帮助开发者将办公应用无缝集成到各种 web 系统中,全面提升用户交互体验。Univer 的核心架构和功能已在 GitHub 上开源。

如果你还没听说过 Univer Go:Univer Go 是 Univer 团队基于 SDK 打造的个性化生产力应用开发工具,目前已开放下载。

官网链接: Univer Go

Univer go 相关文章:

如何使用 Univer 实现工作表或范围保护
Univer Go 推出 AI 辅助编写 Univer API 功能


梦数技术团队
1 声望6 粉丝

上海梦数科技有限公司(简称:梦数科技)成立于2019年,是一家具有国际化视野的高科技公司,专注于提供先进的数字化解决方案,以“串联数据,加速工作流,让业务更高效”为使命,通过全面的软件开发工具,致力于帮...