自訂元件

Web SDK 提供了多種方式來整合自訂元件,使你能夠擴充並客製化 Web SDK 的功能。本指南將介紹幾種常用的方法。

Univer 並不會直接將元件作為參數直接傳遞給任何渲染函式,你需要透過 Facade API 將元件註冊到 Univer 中之後,才能在各個掛載點使用它們。

如果你還不瞭解如何獲取 Facade API 的例項,可以參考 Facade API。

註冊自訂元件

TypeScript
univerAPI.registerComponent(componentKey, CustomComponent, options)

使用 univerAPI.registerComponent 方法來註冊自訂元件。此方法接受三個參數:

  • componentKey: 元件的唯一識別符,用於在 Univer 中引用該元件。
  • CustomComponent: 元件的實現,可以是 React、Vue 或 Web Components。
  • options: 可選的配置選項,用於指定元件所依賴的框架或其他相關設定。

React 元件

註冊 React 元件無需額外的配置,只需確保元件是一個有效的 React 元件即可。以下是一個簡單的範例:

TSX
function ReactComponent(props: Record<string, any>) {  return <div>Hello Univer!</div>}univerAPI.registerComponent('MyReactComponent', ReactComponent)

Vue 元件

Vue 3.x

註冊 Vue 元件時,需要確保安裝並註冊 @univerjs/ui-adapter-vue3 的 UniverVue3AdapterPlugin 插件:

Shell
pnpm add @univerjs/ui-adapter-vue3
TypeScript
import { UniverVue3AdapterPlugin } from '@univerjs/ui-adapter-vue3'univer.registerPlugin(UniverVue3AdapterPlugin)

註冊 Vue 元件時,需要指定 framework 選項為 'vue3'

TSX
const Vue3Component = defineComponent({  setup(props) {    return () => <div>Hello Univer!</div>  },})univerAPI.registerComponent('MyVue3Component', Vue3Component, {  framework: 'vue3',})

Vue 2.x

由於 Vue 2.x 已經不再維護,Univer 暫時沒有提供 Vue 2.x 的 UI 適配器插件的計劃,你可以參考如下程式碼通過自訂插件來實現一個 Vue 2.x 的適配器:

TypeScript
import { DependentOn, Inject, Injector, Plugin } from '@univerjs/core'import { ComponentManager, UniverUIPlugin } from '@univerjs/ui'import Vue from 'vue'/** * 允許 Univer 使用 Vue 2 元件作為 UI 元件的插件。 */@DependentOn(UniverUIPlugin)export class UniverVue2AdapterPlugin extends Plugin {  static override pluginName = 'UNIVER_UI_VUE2_ADAPTER_PLUGIN'  constructor(    private readonly _config = {},    @Inject(Injector) protected readonly _injector: Injector,    @Inject(ComponentManager) protected readonly _componentManager: ComponentManager,  ) {    super()  }  override onStarting(): void {    const { createElement, useEffect, useRef } = this._componentManager.reactUtils    this._componentManager.setHandler('vue2', (component: any) => {      return (props: Record<string, any>) =>        createElement(VueComponentWrapper, {          component,          props: Object.keys(props).reduce<Record<string, any>>((acc, key) => {            if (key !== 'key') {              acc[key] = props[key]            }            return acc          }, {}),          reactUtils: { createElement, useEffect, useRef },        })    })  }}export function VueComponentWrapper(options: {  component: any  props: Record<string, any>  reactUtils: typeof ComponentManager.prototype.reactUtils}) {  const { component, props, reactUtils } = options  const { createElement, useEffect, useRef } = reactUtils  const domRef = useRef<HTMLDivElement>(null)  useEffect(() => {    if (!domRef.current) return    const Constructor = Vue.extend(component)    const instance = new Constructor({      data: props,    })    instance.$mount()    domRef.current.appendChild(instance.$el)    return () => {      instance.$destroy()    }  }, [props])  return createElement('div', { ref: domRef })}

然後將其註冊:

TypeScript
import { UniverVue2AdapterPlugin } from './ui-adapter-vue2'univer.registerPlugin(UniverUIPlugin)univer.registerPlugin(UniverVue2AdapterPlugin)

使用時需要指定 framework 選項為 'vue2'

TSX
const Vue2Component = Vue.component('MyVue2Component', {  template: '<div>Hello, Univer!</div>',})univerAPI.registerComponent('MyVue2Component', Vue2Component, {  framework: 'vue2',})

Web Components

註冊 Web Components 時,需要確保元件符合 Web Components 標準,並安裝和註冊 @univerjs/ui-adapter-web-component 的 UniverWebComponentAdapterPlugin 插件:

Shell
pnpm add @univerjs/ui-adapter-web-component
TypeScript
import { UniverWebComponentAdapterPlugin } from '@univerjs/ui-adapter-web-component'univer.registerPlugin(UniverWebComponentAdapterPlugin)

註冊 Web Components 時,需要指定 framework 選項為 'web-component'

TSX
class WebComponent extends HTMLElement {  constructor() {    super()    const shadow = this.attachShadow({ mode: 'open' })    const div = document.createElement('div')    div.textContent = 'Hello Univer!'    shadow.appendChild(div)  }}univerAPI.registerComponent('my-web-component', WebComponent, {  framework: 'web-component',})

Angular

透過 @angular/elements 的 createCustomElement 將 Angular 元件轉換成自訂元素,再使用 Univer 的 Web Component 適配器註冊,即可將 Angular 元件用於 Univer 側邊欄或對話方塊。

加入與 @angular/core 版本一致的 @angular/elements,以及與其他 Univer 套件版本一致的 @univerjs/ui-adapter-web-component。

初始化 Univer 後呼叫以下函式。傳入宿主元件的 Angular 注入器:使用 private readonly injector = inject(Injector) 取得注入器,再呼叫 registerAngularPanel(univer, univerAPI, this.injector)。宿主元件中的 inject 與 Injector 從 @angular/core 匯入。

TypeScript
import type { Injector } from '@angular/core'import type { FUniver, Univer } from '@univerjs/presets'import { Component } from '@angular/core'import { createCustomElement } from '@angular/elements'import { UniverWebComponentAdapterPlugin } from '@univerjs/ui-adapter-web-component'@Component({  selector: 'app-angular-panel',  standalone: true,  template: '<p>Hello from Angular!</p>',})export class AngularPanelComponent {}export function registerAngularPanel(univer: Univer, univerAPI: FUniver, injector: Injector) {  const element = createCustomElement(AngularPanelComponent, { injector })  univer.registerPlugin(UniverWebComponentAdapterPlugin)  return univerAPI.registerComponent('univer-angular-panel', element, {    framework: 'web-component',  })}

編輯器渲染完成後,使用註冊的元件識別碼作為側邊欄內容:

TypeScript
const sidebar = univerAPI.openSidebar({  header: { title: 'Angular' },  children: { label: 'univer-angular-panel' },  width: 360,})

保存傳回的註冊與側邊欄控制物件,不再使用時呼叫各自的 dispose()。自訂元素在應用程式中註冊一次即可:名稱必須包含連字號,並與 Angular 元件的 selector 不同。

Angular Elements 將輸入映射為元素屬性,將輸出轉換成 DOM CustomEvent 事件。Univer 適配器會傳遞屬性,但不會自動訂閱 Angular 輸出;需要互動時須明確連接事件。

使用自訂元件

透過以下方法,你可以靈活地在 Univer 中整合各種自訂元件,從而增強和定制 Univer 的功能。

注意事項

  1. 在使用這些方法時,請確保 Univer 已經完成渲染。 2. 對於需要註冊的元件,請確保在使用前已正確註冊。 3. 使用 dispose() 方法來清理和移除添加的元件,以避免記憶體洩漏。

添加自訂選單項目

頂部選單列(Ribbon)和右鍵選單(Context Menu)都可以添加自訂元件。要為選單項目添加自訂元件,請建立自訂插件。

替換內建元件

警告

替換內建元件可能會導致一些功能無法正常工作,請在充分閱讀原始碼和文件後自行尋找可替換的元件並謹慎操作。

univerAPI.registerComponent 預設註冊覆寫元件(override: true)。無論外掛在呼叫前或呼叫後註冊內建預設元件,覆寫元件都優先生效。每個 componentKey 同時只能有一個覆寫元件,再次註冊前須先釋放上一個註冊。呼叫回傳值的 dispose() 後,會恢復已註冊的內建預設元件。傳入 { override: false } 則註冊預設元件。

請在對應檢視首次開啟前註冊覆寫元件;變更註冊不會自動重新呈現已掛載的檢視。

例如簡單地替換內建的 ColorPicker 元件:

TSX
// 此程式碼僅可在 UI 層面替換內建 ColorPicker 元件,無法替代其功能實現univerAPI.registerComponent('UI_COLOR_PICKER_COMPONENT', () => <input type="color" />)

作為內容元件添加到……

在側邊欄中使用

使用 univerAPI.openSidebar 方法可以在 Univer 介面中打開一個包含自訂元件的側邊欄。

TSX
// 你應該在合適的時機(比如渲染完成)註冊元件univerAPI.registerComponent('MyCustomSidebarComponent', () => <div>Hello Univer!</div>)const sidebar = univerAPI.openSidebar({  header: { title: 'My Sidebar' },  children: { label: 'MyCustomSidebarComponent' },  onClose: () => {    console.log('close')  },  width: 360,})// 稍後關閉側邊欄sidebar.dispose()

參考: univerAPI.openSidebar

在對話框中使用

使用 univerAPI.openDialog 方法可以打開一個包含自訂元件的對話框。

TSX
// 你應該在合適的時機(比如渲染完成)註冊元件univerAPI.registerComponent('MyCustomDialogComponent', () => <div>Hello Univer!</div>)const dialog = univerAPI.openDialog({  id: 'unique-dialog-id', // 對話框的唯一識別符  draggable: true,  width: 300,  title: { title: 'My Dialog' },  children: {    label: 'MyCustomDialogComponent',  },  destroyOnClose: true,  preservePositionOnDestroy: true,  onClose: () => {},})// 稍後關閉對話框dialog.dispose()

參考: univerAPI.openDialog

你覺得這篇文檔如何?

© 2026 DreamNum Co., Ltd.