自定义组件
Web SDK 提供了多种方式来集成自定义组件,使你能够扩展和定制 Web SDK 的功能。本指南将介绍几种常用的方法。
Univer 并不会直接将组件作为参数传递给任何渲染函数,你需要通过 Facade API 将组件注册到 Univer 中之后,才能在各个挂载点使用它们。
如果你还不了解如何获取 Facade API 的实例,可以参考 Facade API。
注册自定义组件
univerAPI.registerComponent(componentKey, CustomComponent, options)使用 univerAPI.registerComponent 方法来注册自定义组件。这个方法接受三个参数:
componentKey: 组件的唯一标识符,用于在 Univer 中引用该组件。CustomComponent: 组件的实现,可以是 React、Vue 或 Web Components。options: 可选的配置选项,可用于指定组件所依赖的框架或其他相关设置。
React 组件
注册 React 组件无需额外的配置,只需确保组件是一个有效的 React 组件即可。以下是一个简单的示例:
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 插件:
pnpm add @univerjs/ui-adapter-vue3npm install @univerjs/ui-adapter-vue3yarn add @univerjs/ui-adapter-vue3bun add @univerjs/ui-adapter-vue3import { UniverVue3AdapterPlugin } from '@univerjs/ui-adapter-vue3'univer.registerPlugin(UniverVue3AdapterPlugin)注册 Vue 组件时,需要指定 framework 选项为 'vue3'
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 的适配器:
import { DependentOn, Inject, Injector, Plugin } from '@univerjs/core'import { ComponentManager, UniverUIPlugin } from '@univerjs/ui'import Vue from 'vue'/** * The plugin that allows Univer to use Vue 2 components as UI components. */@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 })}然后将其注册:
import { UniverVue2AdapterPlugin } from './ui-adapter-vue2'univer.registerPlugin(UniverUIPlugin)univer.registerPlugin(UniverVue2AdapterPlugin)使用时需要指定 framework 选项为 'vue2'
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 插件:
pnpm add @univerjs/ui-adapter-web-componentnpm install @univerjs/ui-adapter-web-componentyarn add @univerjs/ui-adapter-web-componentbun add @univerjs/ui-adapter-web-componentimport { UniverWebComponentAdapterPlugin } from '@univerjs/ui-adapter-web-component'univer.registerPlugin(UniverWebComponentAdapterPlugin)注册 Web Components 时,需要指定 framework 选项为 'web-component'
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',})使用自定义组件
通过以下方法,你可以灵活地在 Univer 中集成各种自定义组件,从而增强和定制 Univer 的功能。
注意事项
- 在使用这些方法时,请确保Univer已经完成渲染。 2. 对于需要注册的组件,请确保在使用前已正确注册。 3. 使用
dispose()方法来清理和移除添加的组件,以避免内存泄漏。
添加自定义菜单项
顶部菜单栏(Ribbon)和右键菜单(Context Menu)都可以添加自定义组件。请从自己的插件中提供这些菜单项,在使用前注册组件,并随插件一起释放相关注册。
替换内置组件
警告
替换内置组件可能会导致一些功能无法正常工作,请在充分阅读源码和文档后自行寻找可替换的组件并谨慎操作。
通过 univerAPI.registerComponent 方法注册组件时,如果传入的 componentKey 已经存在,那么 Univer 会将其替换为新的组件。
例如简单地替换内置的 ColorPicker 组件:
// 此代码仅可在 UI 层面替换内置 ColorPicker 组件,无法替代其功能实现univerAPI.registerComponent('UI_COLOR_PICKER_COMPONENT', () => <input type="color" />)作为内容组件添加到……
在侧边栏中使用
使用 univerAPI.openSidebar 方法可以在Univer界面中打开一个包含自定义组件的侧边栏。
// 你应该在合适的时机(比如渲染完成)注册组件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.openDialog 方法可以打开一个包含自定义组件的对话框。
// 你应该在合适的时机(比如渲染完成)注册组件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()你觉得这篇文档如何?