React 插槽
本页展示插件插槽的 React 集成。
当您希望外部模块在宿主定义的区域中贡献 ReactNode UI 而无需 fork 您的应用时,可以使用插槽。宿主保持对布局和插槽类型的所有权;插件只能看到您暴露的上下文和属性。
如果您尚未阅读共享模型,请从插件插槽开始。
运行时加载的外部插件
如果您的应用在运行时从磁盘加载插件(例如 await import(fileUrl)),请在应用入口中添加一次此导入:
import "@opentui/react/runtime-plugin-support"
这会安装 Bun 运行时支持,使外部 TS/TSX 插件模块可以针对宿主运行时实例(@opentui/react、React JSX 运行时模块和 Core 运行时模块)进行解析。
在普通 Bun 运行和独立编译的可执行文件中都可以使用此功能。
import "@opentui/react/runtime-plugin-support"
const mod = await import(pathToFileURL(pluginPath).href)
registry.register(mod.loadExternalPlugin())
如果插件需要额外的宿主解析模块,请在副作用导入加载之前配置运行时支持:
import { ensureRuntimePluginSupport } from "@opentui/react/runtime-plugin-support/configure"
ensureRuntimePluginSupport({
additional: {
"my-runtime-module": { marker: "ok" },
},
})
第一方包暴露无副作用的运行时模块映射。对于导入 @opentui/three 的插件,请在宿主应用中安装 @opentui/three 并显式传递其运行时模块:
import { runtimeModules as threeRuntimeModules } from "@opentui/three/runtime-modules"
import { ensureRuntimePluginSupport } from "@opentui/react/runtime-plugin-support/configure"
ensureRuntimePluginSupport({
additional: threeRuntimeModules,
})
副作用导入和可配置入口点安装相同的运行时插件。延迟添加会抛出清晰的错误,而不是被忽略。
React 提供的功能
createReactSlotRegistry(renderer, context, options?)— 创建为ReactNode类型化的注册表。接受与createSlotRegistry相同的SlotRegistryOptions。Slot<TSlots, TContext>— 通用插槽组件,接受registry作为必需属性createSlot(registry, options?)— 可选的便捷辅助函数,返回绑定注册表的<Slot />组件ReactPlugin<TSlots, TContext>— 返回ReactNode的插件的便捷类型别名@opentui/react/runtime-plugin-support— 外部插件/模块加载的一行运行时支持@opentui/react/runtime-plugin-support/configure— 无导入时副作用的可配置运行时支持
直接使用 registry.register() 注册插件——不需要包装函数(不像 Core 的 registerCorePlugin)。
基本用法
import { createCliRenderer } from "@opentui/core"
import { createReactSlotRegistry, createRoot, Slot } from "@opentui/react"
type Slots = {
statusbar: { user: string }
}
const context = { appName: "react-app", version: "1.0.0" }
const renderer = await createCliRenderer()
const registry = createReactSlotRegistry<Slots, typeof context>(renderer, context)
const unregister = registry.register({
id: "clock-plugin",
slots: {
statusbar(ctx, props) {
return <text>{`${ctx.appName}:${props.user}`}</text>
},
},
})
const AppSlot = Slot<Slots, typeof context>
function App() {
return (
<AppSlot registry={registry} name="statusbar" user="sam" mode="replace">
<text>fallback-statusbar</text>
</AppSlot>
)
}
createRoot(renderer).render(<App />)
可选的便捷辅助函数
如果您不想每次都传递 registry,可以绑定一个:
const AppSlot = createSlot(registry)
<Slot> 属性
| 属性 | 类型 | 必需 | 描述 |
|---|---|---|---|
registry | SlotRegistry<ReactNode, Slots, Context> | 是 | 从中解析插件的注册表 |
name | keyof Slots | 是 | 要渲染的插槽 |
mode | SlotMode | 否 | "append"(默认)、"replace" 或 "single_winner"。参见插槽模式。 |
pluginFailurePlaceholder | (failure: PluginErrorEvent) => ReactNode | 否 | 插件抛出错误时的每插槽占位符 UI |
children | ReactNode | 否 | 回退 UI |
| 其余属性 | Slots[name] | — | 插槽特定的属性,转发给插件渲染器 |
ReactSlotOptions(用于 createSlot)
| 选项 | 类型 | 必需 | 描述 |
|---|---|---|---|
pluginFailurePlaceholder | (failure: PluginErrorEvent) => ReactNode | 否 | 插件抛出错误时创建占位符 UI |
插件失败占位符
const Slot = createSlot(registry, {
pluginFailurePlaceholder(failure) {
return <text>{`plugin-error:${failure.pluginId}:${failure.phase}`}</text>
},
})
如果插件抛出错误,插槽会渲染占位符。如果没有提供占位符(或返回 null),插槽会回退到 children。
初始渲染调用期间抛出的插件错误会被内联捕获。React 重新渲染期间抛出的插件错误会被内部错误边界捕获,该边界会在注册表更改时重置。
可观测性
registry.onPluginError((event) => {
console.error(event.pluginId, event.phase, event.source, event.error.message)
})
示例:packages/react/examples/plugin-slots-errors.tsx