Skip to content

AI 场景

模型的产出有三个共同点:流式到达、按行到达、按量计费。这三点恰好都落在虚拟化表格的强项上——它天生只渲染看得见的那几十行,于是「只渲染可见行」顺理成章变成「只为可见行付费」。

三个插件与框架无关,@virt-table/react 直接透传实现与类型。下面都可以直接上手玩。

  • 初始**没有任何列配置**,列是首批数据到达时推断出来的(类型、标题、宽度、对齐都算出来)
  • 吃任何 `AsyncIterable`:SSE、`ReadableStream`、异步生成器都行
  • 一次灌 2 万行也不掉帧(按帧分批落地);往上滚脱离底部跟随,滚回底部自动恢复

试一试 点「开始接收」看列自己长出来,再点「灌 2 万行」压一压。

微应用尚未挂载。

查看完整示例与接真实模型的写法 →

为什么这件事非虚拟化表格不可

拿 AI 列举例:一张 5 万行的表,要给每行生成一句摘要。

普通表格virt-table
DOM 里有多少行5 万行全在只有可视区 + buffer 那几十行
「哪些行需要算」怎么判断判断不了 —— 所有行都"在场"渲染窗口就是答案
首屏要发多少次请求5 万次(或者自己写一套可见性检测)二十来次
用户滚过 10 屏的总开销仍是 5 万次二十来次 × 10
滚走的未完成请求继续跑完、继续计费自动 abort()

差别不是「快一点」,是两个数量级的账单差异。虚拟化在这里不再只是渲染优化,它成了调度的依据。

三个能力怎么配

场景插件谁在动典型用法
让用户用说话代替点筛选器vtAIQuery视图状态(筛选/排序/列显隐)BI 看板、运营后台的自助查询
让模型补一列算不出来的信息vtAIColumn单元格内容工单摘要、情绪分类、字段清洗
让 agent 的结果边跑边显示vtAIStream行与列本身agent 任务结果、日志/事件流

三者互不依赖,可以单独装,也可以叠在同一张表上。

文档站的 demo 没有真的在调模型

上面三个示例里,「模型」都是本地假实现vtAIQuery 用一套关键词规则,vtAIColumn 用模板 + 假延迟。 文档站不该要求访客准备 API key,也不该把 key 放进前端。

它们演示的是链路与调度,而这部分与真模型完全一致:换成一次 fetch 即可,插件其余部分不用动。 vtAIQuery 的假实现还刻意保留了几种真实模型常犯的偏差(填 label 而不是候选值、数字写成字符串、幻觉列名), 好让你看见校验层在干什么。

接真实模型

三个插件的接入点都只有一个回调,服务端形状也都一样:把 prompt 当 system message、jsonSchema 当 tool 的 input_schema,模型返回的参数对象原样回给前端。

tsx
import { VirtTableReact, vtAIQuery, type AIQueryResolveInput } from '@virt-table/react';

export default function AiTable() {
  // 插件实例只能建一次:不放进 useMemo,每次渲染都会新建插件对象
  const plugins = React.useMemo(
    () => [
      vtAIQuery({
        async resolve({ text, prompt, jsonSchema, signal }: AIQueryResolveInput) {
          const r = await fetch('/api/table-query', {
            method: 'POST',
            headers: { 'content-type': 'application/json' },
            body: JSON.stringify({ text, prompt, jsonSchema }),
            signal,
          });
          if (!r.ok) throw new Error(`${r.status} ${r.statusText}`);
          return r.json();
        },
      }),
    ],
    [],
  );

  return <VirtTableReact ref={tableRef} columns={columns} options={{ list, itemKey: 'id', plugins }} />;
}

插件在构造时装载

plugins 在建表时生效,运行时替换该字段不会重新装载。所以回调里要读的开关量走 useRef, 否则拿到的是建表那一刻的闭包值——vtAIColumn 的调用计数就是这么记的。

插件注入的命令式 API(askAITable() / computeAIColumn() / pushAIStreamRows() …)无需手写转发, ref 上的 Proxy 会自动回落到插件 API。

想在服务端先校验一遍,引 @virt-table/vanilla/ai-queryvalidateAIQuery()——那个入口是纯逻辑,不含任何 CSS 与 DOM 依赖。

下一步