TanStack 的“Headless UI”理念
一句话:Headless UI = 只提供逻辑和状态,不规定长成什么样的组件,你用什么 UI 库、什么设计系统都行。
在 TanStack Query 文档 里,你会看到它一直强调:它是一个 data fetching & caching library,而不是一套组件库。这就是 headless 思路:
- 它帮你做:请求、缓存、重试、失效、并发控制、状态机(loading / error / success)
- 它完全不管:要不要用
Table,要不要用Modal,用 Tailwind 还是 MUI 怎么画界面
机制上怎么做到“无头”的?
以 React 为例,useQuery 做的事只有两件:
- 建立一条 查询描述:
queryKey+queryFn(怎么拿数据) - 暴露一堆 状态和操作:
data、error、isLoading、refetch等
它不会替你 render 任何 DOM,也没有 <QueryTable/> 之类的 UI 组件。
你必须自己写组件,把这些状态“长”成你想要的界面。
import { useQuery } from "@tanstack/react-query";
function UserList() {
const { data, isLoading, error } = useQuery({
queryKey: ["users"],
queryFn: () => fetch("/api/users").then((r) => r.json()),
});
if (isLoading) return <div>加载中...</div>;
if (error) return <div>出错了</div>;
return (
<ul>
{data.map((u: any) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}
同样一段逻辑:
- 你可以换成 Chakra、MUI、Ant Design、Tailwind,完全不动 Query 配置
- 你可以把
<ul>换成<Table>,甚至改成图表、卡片,也不用改 Query 的逻辑
为什么这让你自由选择 UI 库 / 设计系统?
因为逻辑层和视图层被硬拆开了:
| 层次 | 谁来负责 | TanStack Query 负责吗? |
|---|---|---|
| API 调用、缓存、重试 | Query | ✅ |
| 什么时候重新拉数据 | Query 策略(失效、焦点、重连) | ✅ |
| div/span/表格/卡片 长什么样 | 你和 UI 库 | ❌ |
| Tailwind / CSS-in-JS / 设计系统 | 你和团队设计规范 | ❌ |
你可以同时满足:
- 对数据:统一用 Query 做缓存、失效策略、乐观更新
- 对 UI:任意换 skin,从裸 HTML 到重型设计系统,不动数据层
官方 Query 站点 本身也体现了这种生态:一堆库(Router、Table、Form、Virtual...),大多都是 headless 能力库,你自己决定如何渲染。
以后看到某个库说自己是 headless,可以直接问一句:
“它是给我状态/逻辑,还是帮我画好组件?”——只给状态/逻辑的,就是真·headless。
1 / 5