Solid 插槽

本页展示插件插槽的 Solid 集成。

当您希望外部模块在宿主定义的区域中贡献 JSX.Element UI 而无需 fork 您的应用时,可以使用插槽。宿主保持对布局和插槽类型的所有权;插件只能看到您暴露的上下文和属性。

如果您尚未阅读共享模型,请从插件插槽开始。

运行时加载的外部插件

如果您的应用在运行时从磁盘加载插件(例如 await import(fileUrl)),请在应用入口中添加一次此导入:

import "@opentui/solid/runtime-plugin-support"

这是一个 Bun 即插即用模块,安装运行时转换支持,使外部 TS/TSX 插件模块使用与宿主应用相同的运行时实例(@opentui/solid@opentui/core@opentui/core/testingsolid-jssolid-js/store)。如果插件导入另一个 OpenTUI 包,请通过 additional 运行时模块提供该包。

在普通 Bun 运行和独立编译的可执行文件中都可以使用此功能。

import "@opentui/solid/runtime-plugin-support"

const mod = await import(pathToFileURL(pluginPath).href)
registry.register(mod.loadExternalPlugin())

如果插件需要额外的宿主解析模块,请在副作用导入加载之前配置运行时支持:

import { ensureRuntimePluginSupport } from "@opentui/solid/runtime-plugin-support/configure"

ensureRuntimePluginSupport({
  additional: {
    "my-runtime-module": { marker: "ok" },
  },
})

第一方包暴露无副作用的运行时模块映射。对于支持 keymap 的 Solid 插件:

import { runtimeModules as keymapRuntimeModules } from "@opentui/keymap/runtime-modules"
import { ensureRuntimePluginSupport } from "@opentui/solid/runtime-plugin-support/configure"

ensureRuntimePluginSupport({
  additional: keymapRuntimeModules,
})

对于导入 @opentui/three 的插件,请在宿主应用中安装 @opentui/three 并显式传递其运行时模块:

import { runtimeModules as threeRuntimeModules } from "@opentui/three/runtime-modules"
import { ensureRuntimePluginSupport } from "@opentui/solid/runtime-plugin-support/configure"

ensureRuntimePluginSupport({
  additional: threeRuntimeModules,
})

副作用导入和可配置入口点安装相同的运行时插件。延迟添加会抛出清晰的错误,而不是被忽略。

Solid 提供的功能

  • createSolidSlotRegistry(renderer, context, options?) — 创建为 JSX.Element 类型化的注册表。接受与 createSlotRegistry 相同的 SlotRegistryOptions
  • Slot<TSlots, TContext> — 通用插槽组件,接受 registry 作为必需属性
  • createSlot(registry, options?) — 可选的便捷辅助函数,返回绑定注册表的 <Slot /> 组件
  • SolidPlugin<TSlots, TContext> — 返回 JSX.Element 的插件的便捷类型别名
  • @opentui/solid/runtime-plugin-support — 外部插件/模块加载的一行运行时支持
  • @opentui/solid/runtime-plugin-support/configure — 无导入时副作用的可配置运行时支持

直接使用 registry.register() 注册插件——不需要包装函数(不像 Core 的 registerCorePlugin)。

基本用法

import { createCliRenderer } from "@opentui/core"
import { createSolidSlotRegistry, Slot, render } from "@opentui/solid"

type Slots = {
  statusbar: { user: string }
}

const context = { appName: "solid-app", version: "1.0.0" }
const renderer = await createCliRenderer()

const registry = createSolidSlotRegistry<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>

const App = () => (
  <AppSlot registry={registry} name="statusbar" user="sam" mode="replace">
    <text>fallback-statusbar</text>
  </AppSlot>
)

render(() => <App />, renderer)

可选的便捷辅助函数

如果您不想每次都传递 registry,可以绑定一个:

const AppSlot = createSlot(registry)

<Slot> 属性

属性类型必需描述
registrySlotRegistry<JSX.Element, Slots, Context>从中解析插件的注册表
namekeyof Slots要渲染的插槽
modeSlotMode"append"(默认)、"replace""single_winner"。参见插槽模式
pluginFailurePlaceholder(failure: PluginErrorEvent) => JSX.Element插件抛出错误时的每插槽占位符 UI
childrenJSX.Element回退 UI
其余属性Slots[name]插槽特定的属性,转发给插件渲染器

SolidSlotOptions(用于 createSlot

选项类型必需描述
pluginFailurePlaceholder(failure: PluginErrorEvent) => JSX.Element插件抛出错误时创建占位符 UI

插件失败占位符

const Slot = createSlot(registry, {
  pluginFailurePlaceholder(failure) {
    return <text>{`plugin-error:${failure.pluginId}:${failure.phase}`}</text>
  },
})

如果插件抛出错误,插槽会渲染占位符。如果没有提供占位符(或返回 null),插槽会回退到 children

初始渲染调用期间抛出的插件错误会被内联捕获。Solid 重新渲染期间抛出的插件错误会被内部 <ErrorBoundary> 捕获,该边界会向注册表报告错误。

可观测性

registry.onPluginError((event) => {
  console.error(event.pluginId, event.phase, event.source, event.error.message)
})

示例:packages/solid/examples/components/plugin-slots-demo.tsx