4.3 KiB
4.3 KiB
L4 · 宿主内 React 组件(entry.renderer)
前置:L3。iframe 页是隔离的「贴进去的网页」;
entry.renderer则是真正的宿主内 React 组件——与内置插件同槽位、同观感、同主题。这是外置插件的最高 UI 形态。
1. 声明
{ "id": "my-plugin", "panel": true, "entry": { "renderer": "renderer.mjs" } }
文件名不要叫 index.ts/index.js——那是 pi 原生扩展的自动发现名,会被 pi CLI 双重加载。
2. renderer.mjs 契约
宿主动态 import 后调用默认导出 mount(ctx):
export default function mount(ctx) {
// ctx.react 是宿主的 React 单实例——必须用它创建组件(自带 React 副本会让 hooks 全部失效)
const R = ctx.react
function Panel() {
const [n, setN] = R.useState(0)
return R.createElement('div', { className: 'p-3 space-y-2' },
R.createElement('div', { className: 'text-[12.5px] text-ink' }, `点击 ${n} 次`),
R.createElement('button', {
className: 'rounded-control border border-line-strong px-2.5 py-1 text-[11.5px] text-ink hover:bg-hover',
onClick: () => setN(n + 1)
}, '+1')
)
}
ctx.registerPanel(Panel) // 右侧面板 tab 内容(需 manifest.panel: true)
ctx.registerView(Panel) // 整页视图(侧栏入口 → 主内容区);两个都注册也可以
}
3. JSX 工程构建配方(推荐)
手写 createElement 只适合玩具;真实插件用 JSX + esbuild,构建时把 react 别名到宿主全局实例:
esbuild renderer.tsx --bundle --format=esm --outfile=renderer.mjs \
--jsx=automatic \
--alias:react=./shims/react.js --alias:react/jsx-runtime=./shims/jsx-runtime.js \
--alias:@aso/plugin-sdk=<sdk源码路径>/index.ts --alias:@aso/plugin-sdk/ui=<sdk源码路径>/ui.tsx
最后两条 alias 只在用到
@aso/plugin-sdk(withTimeout 等)或./ui组件库时需要; sdk 以源码形态发布(exports 指向 .ts/.tsx),esbuild 无法按包名解析,指路径即可。
// shims/react.js —— 从宿主全局取单例
const R = window.__ASO_PLUGIN_HOST__.react
export default R
export const { useState, useEffect, useRef, useMemo, useCallback, createContext,
useContext, Fragment, memo, forwardRef, createElement } = R
// shims/jsx-runtime.js
export const { jsx, jsxs, Fragment } = window.__ASO_PLUGIN_HOST__.jsxRuntime
packages/plugin-template/ 已内置这套配置,复制即用。
4. 硬性约束
| 约束 | 原因 |
|---|---|
React 必须来自 ctx.react / 宿主全局 |
双 React 实例 → hooks 全部报错 |
| 模块自包含(单文件 bundle) | 宿主以 Blob URL 装载,相对导入没有基路径 |
| 不 import 裸包名(react 等) | 同上;一切依赖打进 bundle(react 走别名除外) |
| 单文件 ≤ 8MB | 超限拒绝装载 |
| 仅开发版装载 | 用户版第三方装载关闭(ASO_EXTERNAL_PLUGINS=1 豁免) |
装载失败/白屏/hooks 报错?直接查 常见错误速查 §3。
5. 样式
组件直接跑在宿主树里——宿主的 Tailwind 类与 token 全部可用。标准模式已代码化:优先 import { Button, SettingRow, LoadingState, ErrorState, EmptyState, Switch } from '@aso/plugin-sdk/ui'(组件目录与三态铁律见 L6),不要手写;遵守 DESIGN.md 的类名词汇,禁止硬编码色值/阴影。
6. 生命周期
- 装载:插件列表变化(启停/重载/启动)即重装;
mount同步执行注册 - 卸载:插件禁用/移除 → 注册的组件自动从宿主卸载
- 性能护栏:读源与 import 各限时 8s——超时/失败时槽位呈现宿主统一「装载失败」ErrorState(带重试),见 L7
- 崩溃隔离:渲染错误被
PluginErrorBoundary捕获,不影响宿主
7. 验证
- 重载插件 → 插件页该行出现 ⚙(若已声明设置)/侧栏入口/面板 tab
- 控制台无
[plugins] … renderer 装载失败 - 点击组件内按钮,state 正常更新(hooks 生效 = 单实例约束满足)