React / Vue 接入(薄封装层)
@icen.ai/ui/react 与 @icen.ai/ui/vue 是官方薄封装层,设计哲学是薄如蝉翼:
不复制状态、不重新实现组件逻辑,只做三件事——生命周期接线(mount → create / props 变 → update /
unmount → destroy)、事件桥(onIcen ↔ 回调,卸载自动清理)、类型面 re-export。
底下仍是同一个命令式行为内核,行为层文档与事件契约对两个框架完全适用。
react / react-dom / vue 都是 optional peerDependencies(>=18 / >=3.3)——
只装你用的框架即可。CSS 不在封装层引入,照常先引 tokens 再引组件 CSS。
bun add @icen.ai/ui react react-dom # Vue 项目:bun add @icen.ai/ui vue@import '@icen.ai/ui/tokens.css'; /* 先 tokens */@import '@icen.ai/ui/ui.css'; /* 全量;或按需单引组件 CSS */
import { Chart, DataTable, AiMessage, AiComposer, useIcenEvent } from '@icen.ai/ui/react';function Dashboard({ spec, rows }) { return ( <main> {/* opts 是纯 JSON 契约:变化经稳定比较 → handle.update(原地重渲,保留事件委托) */} <Chart opts={spec} onEvent={(type, detail) => console.log(type, detail)} /> {/* 无 update 内核(createTable)→ opts 变化 destroy + 重建(同一容器) */} <DataTable opts={{ columns, data: rows, pageSize: 10 }} /> <AiMessage opts={{ role: 'assistant', content: '结论如下', meta: '刚刚' }} /> {/* 纯状态绑定:onSend 接发送,消息流自己渲染;给 client + messagesRef 即全托管 */} <AiComposer models={{ providers }} onSend={(text) => send(text)} /> </main> );}// 事件钩:类型推断 + 自动解绑;within 限子树,缺省全局(广播事件只走全局形态)function useCopyToast() { useIcenEvent('icen:copy-success', (detail) => toast.success(`已复制 ${detail.text}`));}
需要命令式句柄(handle.update / exportCSV / stream()…)时传
handleRef={ref} 拿到 ChartHandle / TableHandle / AiToolCallHandle。
'use client' 指令已在入口 banner 打好——RSC 里直接 import 即落在客户端边界内。
<script setup lang="ts">import { ref } from 'vue';import { IcenChart, IcenDataTable, IcenAiMessage, IcenAiComposer } from '@icen.ai/ui/vue';const spec = ref({ type: 'line', title: '趋势', labels, values });// spec deep watch → handle.update(原地重渲);kebab 事件经 emits 透出</script><template> <IcenChart :spec="spec" @chart-hover="onHover" @chart-click="onClick" /> <IcenDataTable :opts="{ columns, data: rows }" @table-row-click="openRow" /> <IcenAiMessage :opts="{ role: 'assistant', content: '结论如下' }" /> <IcenAiComposer :models="{ providers }" @ai-send="send" /></template>
组合式 API 对应物:useIcenInit(init, deps) / useIcenEvent(type, handler)
(onUnmounted 自动清理)。组件 expose getHandle() 取命令式句柄。
- SSR 安全:全部接线在 useEffect / onMounted 里,服务端不执行;行为内核本身也有 SSR 守卫(双保险)。
- StrictMode:React StrictMode 会双跑 effect(setup→cleanup→setup),封装层 create/destroy/create 闭环对得上;内核 init 全部幂等,事件不会重复。
- opts 比较:props.opts 用 JSON.stringify 稳定比较——库内 opts 是纯 JSON 契约;传函数回调(如 columns.render)时 identity 变化不影响,内容变化请换引用。
- 事件广播:
icen:ai-done/icen:ai-usage从 document 派发(传播只经 [document, window]),子树监听收不到——要收广播,useIcenEvent 不传 within / onEvent 在组件上接。 - CSS 照旧:封装层零 CSS;kit 一行入口的 Astro 陷阱提示对框架项目同样有效(Astro 布局 frontmatter 引 CSS)。
封装层覆盖「声明式 props + 生命周期」主路径;以下场景请直接 import 行为模块
(@icen.ai/ui/behaviors/*,在框架的 effect / onMounted 里手动调用):
- 非 React/Vue 技术栈(Svelte / Solid / 原生 Web Components…)——行为层本来就不绑框架。
- 细粒度命令式控制:流式
msg.stream().append()、handle.setData()外部喂数、remote 分页等 handle 高频操作。 - 极热路径(每帧 update 的图表/虚拟滚动)——跳过 props 比较直调
handle.update(spec)。 - 已有命令式代码库渐进迁移——bindComposer / renderAiMessage 等可直接与封装层组件混用(同一内核,同一事件协议)。