Plugins et extensions Facade

Composez les fonctionnalités et étendez contenu et interfaces avec les API publiques.

Univer compose ses produits et fonctionnalités avec des plugins et expose les opérations de contenu par les API Facade. Choisissez moteur, fonctions et interface, ou encapsulez une logique métier dans un plugin avec des méthodes typées accessibles à votre application et aux outils d’un Agent.

Composer les capacités de contenu nécessaires

Un Preset est un ensemble de plugins déjà assemblé pour démarrer rapidement. Le mode Plugin permet à l’application de choisir explicitement produits, fonctions et configuration. Les deux utilisent le même mécanisme.

CoucheResponsabilitéÉléments habituels
Produits et modèles de contenuGérer les Units, structures et opérations fondamentales.Plugins produit de Sheet, Doc, Slide, Relational Table, Canvases et PDF.
Plugins fonctionnelsAjouter des capacités propres à un domaine.Formules, filtres, graphiques, commentaires, etc.
Plugins d’interface et de renduFournir l’affichage et les points d’interaction.Canvas, menus, barres d’outils, raccourcis et composants.
Extensions FacadeExposer des API publiques pour les capacités installées.Opérations sur classeurs, plages, documents et objets, ainsi que des événements sémantiques.

Séparer logique produit et UI permet de réutiliser les capacités compatibles dans Node.js, un Worker ou le navigateur. Un hôte Headless enregistre les plugins de contenu nécessaires ; les API de menus, DOM et interaction nécessitent toujours leur environnement UI.

Dépendances, cycle de vie et propriété des ressources

Les plugins déclarent leurs dépendances directes avec @DependentOn(...) ; le runtime les résout et coordonne leur construction. Une dépendance absente est enregistrée avec sa configuration par défaut. Si elle nécessite une configuration spécifique, configurez-la et enregistrez-la d’abord. Ce mécanisme n’installe pas les paquets et n’importe ni styles, ni langues, ni entrées Facade, ni Workers.

Dans un plugin, l’injection de dépendances (DI) relie services, contrôleurs et modèles par des identifiants publics. Les implémentations de plateforme peuvent être remplacées aux points de configuration ou d’injection prévus, sans obliger la logique métier à construire et gérer toutes ses dépendances.

Cycle de vieTravail approprié
onStarting()Enregistrer dépendances, commandes et contrôleurs ; ne pas lire des Units ou du DOM encore inexistants.
onReady()Initialiser la logique dépendant du modèle quand le contenu correspondant est prêt.
onRendered()Initialiser les extensions ayant besoin du moteur de rendu, de Canvas ou du DOM.
onSteady()Différer le travail non indispensable au premier affichage.

Le propriétaire gère enregistrements de commandes, abonnements, raccourcis et modules de rendu. Utilisez disposeWithMe() lorsqu’un enregistrement retourne un disposable pour le libérer avec le plugin ou contrôleur. Actuellement, mergeMenu() retourne void, pas un disposable.

Des entrées différentes, un même mécanisme de modification

Barres d’outils, raccourcis et API métier peuvent appeler la même commande. Un hôte Agent réutilise cette entrée après installation des capacités requises. Le système sépare intention métier, modifications persistantes et interaction temporaire.

TypeResponsabilitéExemple
COMMANDValider les cibles et orchestrer étapes métier, Mutations ou Operations.Écrire un rapport de ventes ou supprimer une ligne.
MUTATIONModifier le modèle avec des paramètres explicites et sérialisables, pour la relecture et le traitement collaboratif correspondant.Modifier des cellules ou insérer une ligne.
OPERATIONModifier l’état d’interaction temporaire.Changer sélection, défilement ou panneau.
100%

Par exemple, FRange.setValues() appelle SetRangeValuesCommand, qui produit une SetRangeValuesMutation et prépare les données d’annulation. Les différentes entrées suivent le même chemin et réutilisent validation, intercepteurs et traitement des modifications. L’annulation, la collaboration et l’historique nécessitent leurs mécanismes respectifs : enregistrer une Mutation ne les fournit pas automatiquement.

Exposer les capacités d’un plugin par Facade

Facade propose des objets publics orientés contenu tout en masquant la recherche des services internes et la préparation des paramètres de commande. Une extension peut composer des méthodes existantes ou appeler sa propre commande métier.

Cette extension ajoute generateSalesReport() à FUniver. La commande est implémentée dans la section suivante. Les trois parties apportent la méthode d’exécution, son enregistrement et les types 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() installe la méthode à l’exécution ; l’augmentation de module ajoute uniquement les types. Le consommateur doit exécuter le fichier avec un import à effet de bord. Exporter la classe ou enregistrer le plugin n’exécute pas une autre entrée Facade. Un paquet publié peut exposer un sous-chemin /facade et préserver cet enregistrement avec package.json#sideEffects.

Ce mécanisme concerne uniquement les classes Facade prenant explicitement en charge extend(). Une classe exportée n’est pas nécessairement une cible mixin. Choisissez des noms métier clairs et évitez les collisions avec des méthodes ou extensions existantes.

Exemple : un même rapport de ventes pour l’application et les Agents

Ce plugin minimal écrit des données de ventes en anglais, des formules de synthèse et un style d’en-tête dans A1:D5 du Sheet indiqué, en remplaçant cette plage. Il reçoit les ID explicites du classeur et de la feuille, sans dépendre de la sélection ou du focus.

La commande vérifie cible et dimensions puis appelle SetRangeValuesCommand pour réutiliser ses modifications de contenu et son annulation/rétablissement. Le plugin enregistre la commande dans onStarting() et conserve son disposable. Les données fixes servent à la démonstration ; une application peut utiliser des entrées métier validées.

Exemple de résultat avec couleurs, format numérique et dimensions des lignes et colonnes ajustés pour la lisibilité.

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));  }}

Configurez les produits et les formules selon le guide d’installation Sheet, puis enregistrez le plugin, importez son extension Facade et appelez-la. Ici, univer est une instance déjà configurée. Pour Node.js, consultez 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

Le succès de la commande indique que l’écriture est terminée ; attendez l’application des résultats du calcul avant de lire les valeurs des formules. Dans cet exemple, D5 vaut 719000.

Un bouton d’application peut appeler generateSalesReport(target) depuis son gestionnaire de clic. Un outil Agent peut appeler la même méthode après installation du plugin et de l’extension Facade dans son hôte Headless. L’action métier et la cible sont identiques ; l’UI fournit l’entrée et le retour utilisateur. L’installation du plugin ne publie pas automatiquement un outil Agent : l’application gère l’enregistrement de l’outil et l’autorisation du Unit.

Limites d’implémentation et ressources complémentaires

  • Réutiliser les commandes de contenu existantes : cet exemple utilise SetRangeValuesCommand. Une nouvelle Mutation nécessite des opérations inverses et l’intégration collaborative appropriée. De nouvelles ressources persistantes nécessitent aussi une sérialisation ; ne modifiez pas directement le modèle.
  • Séparer exécution, calcul et soumission : plusieurs appels Facade ne forment pas automatiquement une transaction atomique. L’exemple écrit une plage en une seule fois ; un hôte collaboratif soumet et confirme les changements selon le contrat du Runtime.
  • Composer selon l’environnement : intégrez séparément contenu et UI, gardez les paquets du même train de publication Univer à la même version et utilisez les ressources linguistiques pour l’interface. L’application conserve l’autorisation et la validation des paramètres des entrées Agent.

En savoir plus : Architecture Univer · Configuration Facade · Référence Facade · Personnalisation UI · Agents et automatisation

Retour aux fonctionnalités

Que pensez-vous de ce guide ?

© 2026 DreamNum Co., Ltd.