Плагины и расширения Facade

Собирайте функции и расширяйте контент и интерфейс через публичные API.

Univer собирает продукты и функции из плагинов и предоставляет операции с контентом через Facade API. Можно выбрать нужные движок, функции и интерфейс либо оформить бизнес-логику в плагин с типизированными методами для приложения и инструментов Agent.

Сборка необходимых возможностей работы с контентом

Preset — заранее собранный набор плагинов для быстрого старта. В режиме Plugin приложение явно выбирает продукты, функции и настройки. Оба подхода используют один механизм плагинов.

УровеньОтветственностьТипичные компоненты
Продукты и модели контентаУправление Unit, структурами и базовыми операциями.Плагины Sheet, Doc, Slide, Relational Table, Canvases и PDF.
Функциональные плагиныВозможности определённой предметной области.Формулы, фильтры, диаграммы, комментарии и другое.
Плагины UI и рендерингаОтображение и точки взаимодействия.Canvas, меню, панели инструментов, сочетания клавиш и компоненты.
Расширения FacadeПубличные API установленных возможностей.Операции с книгами, диапазонами, документами и объектами, семантические события.

Разделение логики продукта и UI позволяет повторно использовать совместимые возможности в Node.js, Worker и браузере. Headless-хост регистрирует нужные плагины контента; API меню, DOM и взаимодействия по-прежнему требуют соответствующей UI-среды.

Зависимости, жизненный цикл и владение ресурсами

Плагины объявляют прямые зависимости через @DependentOn(...), а runtime разрешает их и определяет порядок создания. Отсутствующие плагины зависимостей регистрируются с настройками по умолчанию. Если нужны свои настройки, сначала настройте и зарегистрируйте зависимость. Этот механизм не устанавливает пакеты и не импортирует стили, локализации, точки входа Facade или Worker.

Внутри плагина внедрение зависимостей (DI) связывает сервисы, контроллеры и модели через публичные идентификаторы сервисов. Реализации для конкретной платформы можно заменить в предусмотренных точках настройки или внедрения, не заставляя бизнес-логику создавать и обслуживать все зависимости.

ЭтапПодходящая работа
onStarting()Регистрация зависимостей, команд и контроллеров; нельзя обращаться к ещё не созданным Unit или DOM.
onReady()Инициализация логики модели после готовности соответствующего контента.
onRendered()Инициализация расширений, которым нужны рендерер, Canvas или DOM.
onSteady()Отложенная работа, не обязательная для первого отображения.

Регистрациями команд, подписками, сочетаниями клавиш и модулями рендеринга управляет их владелец. Используйте disposeWithMe() для регистраций, возвращающих disposable, чтобы освобождать ресурсы вместе с плагином или контроллером. Сейчас mergeMenu() возвращает void, а не disposable.

Разные точки входа, единый механизм изменения контента

Панель инструментов, сочетания клавиш и бизнес-API могут вызывать одну команду. Хост Agent также может использовать её после установки необходимых возможностей. Система разделяет бизнес-намерение, сохраняемые изменения и временное состояние интерфейса.

ТипОтветственностьПример
COMMANDПроверка цели и организация бизнес-шагов, Mutation или Operation.Создание отчёта о продажах, удаление строки.
MUTATIONИзменение модели по явным сериализуемым параметрам для воспроизведения и соответствующей совместной обработки.Изменение ячеек, вставка строки.
OPERATIONИзменение временного состояния взаимодействия.Выделение, прокрутка, состояние панели.
100%

Например, FRange.setValues() вызывает SetRangeValuesCommand, которая формирует SetRangeValuesMutation и данные отмены. Разные точки входа используют один путь выполнения и повторно применяют проверки, перехватчики и обработку изменений. Отмена, совместная работа и история требуют своих механизмов; одной регистрации Mutation для этого недостаточно.

Публикация возможностей плагина через Facade API

Facade предоставляет публичные объекты для работы с контентом, скрывая поиск внутренних сервисов и подготовку параметров команд. Расширение может объединять существующие методы Facade или вызывать собственную бизнес-команду.

Пример добавляет generateSalesReport() в FUniver; команда реализована в следующем разделе. Три части отвечают за метод во время выполнения, его регистрацию и типы TypeScript.

sales-report.facade.ts

TypeScript
import { FUniver } from '@univerjs/core/facade';import type { ISalesReportParams } from './sales-report.plugin';import { GenerateSalesReportCommand } from './sales-report.plugin';export interface IFUniverSalesReport {  generateSalesReport(params: ISalesReportParams): Promise<boolean>;}export class FUniverSalesReport extends FUniver implements IFUniverSalesReport {  override generateSalesReport(params: ISalesReportParams): Promise<boolean> {    return this._commandService.executeCommand(GenerateSalesReportCommand.id, params);  }}FUniver.extend(FUniverSalesReport);declare module '@univerjs/core/facade' {  interface FUniver extends IFUniverSalesReport {}}

FUniver.extend() устанавливает метод во время выполнения; расширение модуля добавляет только типы. Пользователь должен выполнить файл расширения через импорт с побочным эффектом. Экспорт класса или регистрация плагина не выполняют отдельную точку входа Facade. Публикуемый пакет может предоставить путь /facade и сохранить регистрацию через package.json#sideEffects.

Такой подход применим только к классам Facade, явно поддерживающим extend(). Экспортированный класс не обязательно поддерживает mixin. Используйте понятные бизнес-имена методов и избегайте конфликтов с существующими API и расширениями.

Пример: один отчёт о продажах для приложения и Agent

Минимальный плагин записывает английские данные о продажах, итоговые формулы и стиль заголовка в A1:D5 указанного Sheet, заменяя этот диапазон. Он принимает явные ID книги и листа, не полагаясь на выделение или фокус.

Команда проверяет цель и размеры, затем вызывает SetRangeValuesCommand, повторно используя изменение контента и отмену/повтор. Плагин регистрирует команду в onStarting() и управляет её disposable. Фиксированные данные служат примером; приложение может заменить их проверенными бизнес-данными.

Пример результата. Для удобства чтения настроены цвета, числовой формат, высота строк и ширина столбцов.

sales-report.plugin.ts

TypeScript
import type { ICellData, ICommand, Workbook } from '@univerjs/core';import {  CommandType, DependentOn, ICommandService, Inject, Injector,  IUniverInstanceService, Plugin, UniverInstanceType,} from '@univerjs/core';import { SetRangeValuesCommand, UniverSheetsPlugin } from '@univerjs/sheets';export interface ISalesReportParams {  unitId: string;  subUnitId: string;}export const GenerateSalesReportCommand: ICommand<ISalesReportParams> = {  id: 'sales-report.command.generate',  type: CommandType.COMMAND,  handler: (accessor, params) => {    if (!params) return false;    const sheet = accessor.get(IUniverInstanceService)      .getUnit<Workbook>(params.unitId, UniverInstanceType.UNIVER_SHEET)      ?.getSheetBySheetId(params.subUnitId);    if (!sheet || sheet.getRowCount() < 5 || sheet.getColumnCount() < 4) return false;    const value: ICellData[][] = [      ['Region', 'Q1', 'Q2', 'H1 Total'].map((v) => ({        v, t: 1, s: { bg: { rgb: '#17324D' }, cl: { rgb: '#FFFFFF' }, bl: 1 },      })),      [{ v: 'East', t: 1 }, { v: 128000, t: 2 }, { v: 142000, t: 2 }, { f: '=SUM(B2:C2)' }],      [{ v: 'West', t: 1 }, { v: 96000, t: 2 }, { v: 118000, t: 2 }, { f: '=SUM(B3:C3)' }],      [{ v: 'North', t: 1 }, { v: 110000, t: 2 }, { v: 125000, t: 2 }, { f: '=SUM(B4:C4)' }],      [{ v: 'Total', t: 1 }, { f: '=SUM(B2:B4)' }, { f: '=SUM(C2:C4)' }, { f: '=SUM(D2:D4)' }],    ];    return accessor.get(ICommandService).executeCommand(SetRangeValuesCommand.id, {      ...params,      range: { startRow: 0, endRow: 4, startColumn: 0, endColumn: 3 },      value,    });  },};@DependentOn(UniverSheetsPlugin)export class SalesReportPlugin extends Plugin {  static override pluginName = 'SALES_REPORT_PLUGIN';  static override type = UniverInstanceType.UNIVER_SHEET;  constructor(    _config: undefined,    @Inject(Injector) protected override readonly _injector: Injector,    @ICommandService private readonly _commands: ICommandService,  ) { super(); }  override onStarting(): void {    this.disposeWithMe(this._commands.registerCommand(GenerateSalesReportCommand));  }}

Сначала настройте продукты и формулы по руководству установки Sheet, затем зарегистрируйте плагин, импортируйте расширение Facade и вызовите его. Здесь univer — уже настроенный экземпляр. Настройка Node.js описана в Headless Web SDK.

TypeScript
import { FUniver } from '@univerjs/core/facade';import '@univerjs/sheets/facade';import '@univerjs/engine-formula/facade';import { SalesReportPlugin } from './sales-report.plugin';import './sales-report.facade';univer.registerPlugin(SalesReportPlugin);const univerAPI = FUniver.newAPI(univer);const workbook = univerAPI.createWorkbook({ name: 'Quarterly Sales' });const worksheet = workbook.getActiveSheet();const target = { unitId: workbook.getId(), subUnitId: worksheet.getSheetId() };const generated = await univerAPI.generateSalesReport(target);if (!generated) throw new Error('Sales report could not be generated');await univerAPI.getFormula().onCalculationResultApplied();console.log(worksheet.getRange('D5').getValue()); // 719000

Успех команды означает завершение записи. Прежде чем читать значения формул, дождитесь применения результатов вычислений. В примере D5 равно 719000.

Кнопка приложения может вызывать generateSalesReport(target) из обработчика нажатия. Обработчик инструмента Agent может вызвать тот же метод после установки плагина и расширения Facade в Headless-хост. Бизнес-действие и цель общие; UI обеспечивает вход и обратную связь. Установка плагина не публикует инструмент Agent автоматически: приложение регистрирует инструмент и проверяет доступ к Unit.

Границы реализации и дополнительные материалы

  • Повторно используйте существующие команды: пример использует SetRangeValuesCommand. Новая Mutation требует обратных операций и соответствующей интеграции совместной работы. Для новых сохраняемых ресурсов нужна также сериализация; не изменяйте модель напрямую.
  • Различайте выполнение, вычисление и отправку: несколько вызовов Facade не образуют автоматическую атомарную транзакцию. Здесь используется одна запись диапазона; совместный хост отправляет и подтверждает изменения по контракту Runtime.
  • Собирайте под нужную среду: отдельно подключайте контент и UI, используйте одинаковую версию пакетов одной релизной линейки Univer и языковые ресурсы для UI. Для входов Agent приложение отвечает за авторизацию и проверку параметров.

Подробнее: Архитектура Univer · Подключение Facade · Справочник Facade · Настройка UI · Agent и автоматизация

Вернуться к обзору функций

Насколько полезно это руководство?

© 2026 DreamNum Co., Ltd.