React / Vue 接入(薄封装层)

@icen.ai/ui/react 与 @icen.ai/ui/vue 是官方薄封装层,设计哲学是薄如蝉翼: 不复制状态、不重新实现组件逻辑,只做三件事——生命周期接线(mount → create / props 变 → update / unmount → destroy)、事件桥(onIcen ↔ 回调,卸载自动清理)、类型面 re-export。 底下仍是同一个命令式行为内核,行为层文档与事件契约对两个框架完全适用。

安装install

react / react-dom / vue 都是 optional peerDependencies(>=18 / >=3.3)—— 只装你用的框架即可。CSS 不在封装层引入,照常先引 tokens 再引组件 CSS。

bash
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 */
React@icen.ai/ui/react
tsx
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 即落在客户端边界内。

Vue@icen.ai/ui/vue
vue
<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 注意notes
  • 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)。
何时直接用行为层escape hatch

封装层覆盖「声明式 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 等可直接与封装层组件混用(同一内核,同一事件协议)。